#############################################################
# overview
#############################################################


# This week, we will introduce a couple new types of objects in Python:
# dictionaries and tuples. Like lists, these are "container" types, in
# that they do not store elements directly, but rather references to
# other objects. However, they have different structure and properties
# in terms of how they store and access data.

# To set that up that discussion, this pre-lecture code presents a
# scenario that we implement using only lists. Then in lecture, we will
# revisit and see how implementing with dictionaries can improve the
# implementation.


#############################################################
# scenario
#############################################################


# Imagine you are the MIT registrar. One of your responsibilities is to
# maintain the subject listing, which contains information on each
# subject's number, name, units, etc. Ultimately, that info is displayed
# on webpages like the one below.

#   http://student.mit.edu/catalog/m6a.html

# Typically, such a webpage is not written by hand, but rather
# programmatically by applying a template to each subject's info. That
# info needs to be stored and accessed according to some structure, and
# there are many ways to do so. Suppose the registrar uses Python,
# though. What would be a reasonable way to represent that data?

# For starters, we could record each subject's info in a list. Here's an
# example for our subject and for 6.100A, where we've stored the subject
# number, name, units, and description. All are strings, except for the
# number of units, which is an int.


subject_6100 = [
    "6.100", "Intro to Programming and CS", 12,
    "Develops foundational skills in programming...",
]
subject_6100A = [
    "6.100A", "Intro to CS Programming", 6,
    "Introduction to computer science and programming...",
]


# And here is a collection of subjects, using the same format.


subjects = [
    subject_6100,
    subject_6100A,
    ["6.100B", "Intro to Computational Thinking", 6, "Provides an introduction to using computation..."],
    ["6.101", "Fundamentals of Programming", 12, "Introduces fundamental concepts of programming..."],
    ["6.120", "Math for Computer Science", 12, "Elementary discrete mathematics for..."],
    ["6.121", "Intro to Algorithms", 12, "Introduction to mathematical modeling of..."],
    ["6.190", "Intro to Low-level Programming", 6, "Introduction to C and assembly language..."],
    ["6.191", "Computation Structures", 12, "Provides an introduction to the design..."],
]


# As long as each subject's list record is in the same format, we can
# reliably retrieve info about any subject. Below are some functions
# demonstrating that.


def get_subject_name(subjects, number):
    for record in subjects:
        if record[0] == number:
            return record[1]


def get_subject_units(subjects, number):
    for record in subjects:
        if record[0] == number:
            return record[2]


def get_subject_description(subjects, number):
    for record in subjects:
        if record[0] == number:
            return record[3]


# print()
# print(get_subject_name(subjects, "6.100"))
# print(get_subject_name(subjects, "6.100B"))
# print(get_subject_units(subjects, "6.190"))
# print(get_subject_description(subjects, "6.121"))


# Suppose now we'd like to store information about prereqs as well.
# Perhaps it seems natural to insert that in each subject's record right
# after the number of units. For example:


subject_6190 = [
    "6.190", "Intro to Low-level Programming", 6, ["6.100A"],
    "Introduction to C and assembly language...",
]
subject_6121 = [
    "6.121", "Intro to Algorithms", 12, ["6.100A", "6.120"],
    "Introduction to mathematical modeling of...",
]


# Note that some subjects may have more than one prereq, like in the
# case of 6.121. Hence, if we want to reliably retrieve prereq
# information from a single index location, it's worth storing them as
# lists of prereq numbers. This means when a subject has no prereqs, we
# should store an empty list. (Btw, actual prereq structure can be
# rather complex, due to alternative options. We're not considering that
# in our example.)

# Here is an updated collection of our subject records in the new
# format:


subjects = [
    ["6.100", "Intro to Programming and CS", 12, [], "Develops foundational skills in programming..."],
    ["6.100A", "Intro to CS Programming", 6, [], "Introduction to computer science and programming..."],
    ["6.100B", "Intro to Computational Thinking", 6, ["6.100A"], "Provides an introduction to using computation..."],
    ["6.101", "Fundamentals of Programming", 12, ["6.100"], "Introduces fundamental concepts of programming..."],
    ["6.120", "Math for Computer Science", 12, ["18.01"], "Elementary discrete mathematics for..."],
    subject_6121,
    subject_6190,
    ["6.191", "Computation Structures", 12, ["8.02", "6.100A", "6.190"], "Provides an introduction to the design..."],
]


# Because we've squeezed in an additional element for each record, our
# `get_subject_description()` function no longer retrieves descriptions,
# but instead prereq lists. So we should update that:


get_subject_prereqs = get_subject_description


def get_subject_description(subjects, number):
    for record in subjects:
        if record[0] == number:
            return record[4]


# print()
# print(get_subject_name(subjects, "6.191"))
# print(get_subject_units(subjects, "6.191"))
# print(get_subject_prereqs(subjects, "6.191"))
# print(get_subject_description(subjects, "6.191"))
# print(get_subject_prereqs(subjects, "6.100A"))
# print(get_subject_description(subjects, "6.100A"))


# Okay, at this point, you should feel there are potentially several
# problems with this code.

#  1. The strategy of relying on specific index values for each piece of
#     data about a subject is fragile.
#    a. If we hadn't realized that `get_subject_description()` was no
#       longer valid, we might generate subject listing webpages with no
#       descriptions.
#    b. There's also the danger of only adding prereqs for some subjects
#       but not others. Then, our records would become inconsistent in
#       structure, and our `get_subject_prereqs()` would sometimes
#       retrieve descriptions, and vice versa.

#  2. There's also the issue of performance. Every time we look up info
#     about a subject, we traverse the entire `subjects` list. We might
#     get lucky and find the subject we're looking for early on. But we
#     might also get unlucky and have to traverse nearly all the the way
#     to the end. If this was the entire course catalog, that would seem
#     unnecessarily expensive.

#  3. Finally, we should also be uncomfortable that our accessor
#     functions are all identical except for the index number being
#     looked up. There may be a way to reduce duplication using a helper
#     function, but we'd still have to associate a specific index with
#     each accessor.

# We might partially address issue 1b by flipping the order in which we
# index into the top-level `subjects` list. Namely, suppose we made
# `subjects[0]` the list of all subject numbers. Then `subjects[1]`
# would be the list of all subject names, and so on for `subjects[2]`,
# `subjects[3]`, etc.


subjects = [
    ["6.100", "6.100A", "6.100B", "6.101", "6.120", "6.121", "6.190", "6.191"],
    [
        "Intro to Programming and CS",
        "Intro to CS Programming",
        "Intro to Computational Thinking",
        "Fundamentals of Programming",
        "Math for Computer Science",
        "Intro to Algorithms",
        "Intro to Low-level Programming",
        "Computation Structures",
    ],
    [12, 6, 6, 12, 12, 12, 6, 12],
    [
        [], [], ["6.100A"], ["6.100"],
        ["18.01"], ["6.100A", "6.120"],
        ["6.100A"], ["8.02", "6.100A", "6.190"],
    ],
    [
        "Develops foundational skills in programming...",
        "Introduction to computer science and programming...",
        "Provides an introduction to using computation...",
        "Introduces fundamental concepts of programming...",
        "Elementary discrete mathematics for...",
        "Introduction to mathematical modeling of...",
        "Introduction to C and assembly language...",
        "Provides an introduction to the design...",
    ],
]


# In this new structure, a first simple check of consistency could be
# that each nested list has the same length. However, it'd be incumbent
# on us to make sure all such list contain data for the subjects **in
# the same order.** Thus, there's still the danger of records becoming
# out of sync. And if that's the case, this alternative format probably
# makes it harder on ourselves to inspect and debug.


# EXERCISE: Rewrite the `get_subject_prereqs()` function assuming this
# new structure for `subjects`.


def get_subject_prereqs(subjects, number):
    ...  # TODO


# print()
# print(get_subject_prereqs(subjects, "6.100A"))
# print(get_subject_prereqs(subjects, "6.100B"))
# print(get_subject_prereqs(subjects, "6.120"))
# print(get_subject_prereqs(subjects, "6.121"))


# In the end, the takeaway is that imposing rigid requirements on list
# indexing may not be the best way to go about storing and retrieving
# structured data. In lecture, we'll show a better way using the concept
# of **mapping** or **associating** between pieces of data, which Python
# provides through its `dict` object type.
