| January 2026 | ||||||
|---|---|---|---|---|---|---|
| U | M | T | W | R | F | S |
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tue 13 Jan Lecture 0 |
Welcome and Course Introduction
We outline the course, its goals, and talk about various administrative
issues.
Readings:
A mysterious function ...
We examine a program we know nothing about, making hypotheses about
what it is supposed to do. We notice that this function has no
meaningful output for some inputs, which leads us to restricting its
valid inputs using preconditions. We use a similar mechanism,
postconditions, to describe the value it returns. Along the way, we
get acquainted to C0 and its support for checking pre- and
post-conditions. We then notice that this function doesn't return
the expected outputs even for some valid inputs ...
Concepts:
|
| Thu 15 Jan Lecture 1 |
Contracts
Contracts are program annotations that spell out what the code is
supposed to do. They are the key to connecting algorithmic ideas to
their implementation as a program. In this lecture, we illustrate
the use of contracts by means of a simple C0 program. As we do so,
we learn to verify loop invariants — an important type of
contract, we see how contracts can help us write correct code, and
we get acquainted with C0's automated support to validating contracts.
Concepts:
|
| Fri 16 Jan precept 1 |
What's the point?
This precept will help you become comfortable proving the
correctness of a function using point-to reasoning.
|
| Tue 20 Jan Lecture 2 |
Ints
In this lecture, we explore how number representation interplays
with the the ability to write effective contracts. We focus on
integers and see how the binary representation called two's
complement supports the laws of modular arithmetic, which C0
embraces. We also examine operations that allow exploiting the bit
pattern of a binary number to achieve compact representations of
other entities of interest, and to manipulate them.
|
| Thu 22 Jan Lecture 3 |
Arrays
In this lecture, we examine arrays as our first composite data
structure, i.e., a data construction designed to hold multiple
values, together with operations to access them. Accessing an array
element outside of its range is undefined — it is a safety
violation — and we see how to use contracts, in particular
loop invariants, to ensure the safety of array accesses in a
program. Arrays are stored in memory, which means that they are
manipulated through an address. This raises the possibility of
aliasing, a notorious source of bugs in carelessly written programs.
|
| Fri 23 Jan precept 2 |
int-eresting!
This precept provide pratice on C0 integers (two's complement,
bit patterns) and arrays (memory diagrams, safety proofs).
|
| Tue 27 Jan Lecture 4 |
Searching Arrays
We practice reasoning about arrays by implementing a function that
searches whether an array contains a given value — this is the
gateway to a whole class of useful operations. We notice that this
function returns its result more quickly when the array is sorted.
We write a specialized variant that assumes that the array is
sorted, and show that it works correctly by reasoning about array
bounds. The first (simpler but less efficient) version acts as a
specification for the the second (slightly more complex but often
faster). Using the specification in the contract for the
implementation is a standard technique to help writing correct code.
|
| Thu 29 Jan Lecture 5 |
Big-O
We examine big-O notation as a mathematical tool to describe the
running time of algorithms, especially for the purpose of comparing
two algorithms that solve the same problem. As a case study, we use
the problem of sorting an array, and for now a single sorting
algorithm, selection sort. As we implement selection sort, we see
that starting with contracts gives us high confidence that the
resulting code will work correctly. Along the way, we develop a
useful library of functions about sorted arrays to be used in
contracts.
|
| Fri 30 Jan precept 3 |
Hip Hip Array!
In this precept, your group will practice testing and reasoning
about array code, as well as determining the complexity of
functions.
|
| Tue 3 Feb Lecture 6 |
Binary search
When searching for a value in a sorted array, examining the middle
element allows us to discard half of the array in the worst case.
The resulting algorithm, binary search, has logarithmic complexity
which is much better than linear search (which is linear).
Achieving a correct imperative implementation can be tricky however,
and we use once more contracts as a mechanism to reach this goal.
|
| Thu 5 Feb Lecture 7 |
Quicksort
We use the key idea underlying binary search to implement two sorting
algorithms with better complexity than selection sort. We examine
one of them, quicksort, in detail, again using contracts to achieve
a correct implementation, this time a recursive implementation. We
observe that the asymptotic complexity of quicksort depends on the
the value of a quantity the algorithm use (the pivot) and discuss
ways to reduce the chances of making a bad choice for it. We
conclude by examining another sorting algorithm, mergesort, which is
immune from this issue.
|
| Fri 6 Feb precept 4 |
Fancy Partition
In this precept, your group will implement the partition function
used in quicksort and prove aspects of its safety and correctness.
|
| Tue 10 Feb Lecture 8 |
Libraries
Arrays are homogeneous collections, where all components have the
same type. structs enable building heterogeneous
collections, that allow combining components of different types.
They are key to building pervasively used data structures. In C0,
a struct resides in allocated memory and is accessed
through an address, which brings up a new form of safety violation:
the NULL pointer violation. We extend the language of
contracts to reason about pointers.
Now that we have two ways to build complex collections, we start exploring the idea of segregating the definition of a data structure and the operations that manipulate it into a library. Code that uses this data structure only needs to be aware of the type, operations and invariants of the data structure, not the way they are implemented. This is the basis of a form of modular programming called abstract data types, in which client code uses a data structure exclusively through an interface without being aware of the underlying implementation.
|
| Thu 12 Feb Lecture 9 |
Stacks and Queues
In this lecture, we examine the interface of two fundamental data
structures, stacks and queues. We practice using the exported
functions to write client code that implements operations of stacks
and queues that are not provided by the interface. By relying only
of the interface functions and their contracts, we can write code
that is correct for any implementation of stacks and queues.
|
| Fri 13 Feb precept 5 |
Point me to Safety
In this precept, your group will practice working with memory
diagrams and gain confidence in understanding the organization of
libraries into an interface and one or more implementations.
|
| Tue 17 Feb Lecture 10 |
Linked Lists
We observe that we can implement array-like collections using
a struct that packages each element with a pointer to the
next element. This idea underlies linked lists, a data structure
pervasively used in computer science. Writing correct code about
linked lists is however tricky as we often rely on stricter
invariants than natively supported, in particular the absence of
cycles. We develop code to be used in contracts to check for common
such properties. We then use linked lists to write code that
implements the stack interface, and similarly for queues. We could
have given an array-based implementation, and we note the advantages
and drawbacks of each choice.
|
| Thu 19 Feb Lecture 11 |
Unbounded Arrays
When implementing a data structure for a homogeneous collection,
using an array has the advantage that each element can be accessed
in constant time, but the drawback that we must fix the number of
elements a priori. Linked lists can have arbitrary length but
access takes linear time. Can we have the best of both worlds?
Unbounded arrays rely on an array to store the data, but double it
when we run out of place for new elements. The effect is that
adding an element can be either very cheap or very expensive
depending on how full the array is. However, a series of insertions
will appear as if each one of them takes constant time on average.
Showing that this is the case requires a technique called amortized
analysis, which we explore at length in this lecture.
|
| Fri 20 Feb precept 6 |
Link it all Together
In this precept, your group will practice working with linked lists
and dive into amortized analysis.
|
| Tue 24 Feb Lecture 12 |
Hashing
Associative arrays are data structures that allow efficiently
retrieving a value on the basis of a key: arrays are the special
case where valid indices into the array are the only possible keys.
One popular way to implement associative arrays is to use a hash
table, which computes an array index out of each key and uses that
index to find the associated value. However, multiple keys can map
to the same index, something called a collision. We discuss several
approaches to dealing with collisions, focusing on one called separate
chaining. The cost of access depends on the contents of the hash
table. While a worst case analysis is useful, it is not typically
representative of normal usage. We compute the average case
complexity of an access relative to a few simple parameters of the
hash table.
|
| Thu 26 Feb Lecture 13 |
Hash Dictionaries
In this lecture, we look at the interface of modular code at greater
depth, using hash functions as a case study. In this and many
example, it is useful for the client to fill in some parameters used
by the library code so that it delivers the functionalities needed
by the client. One such parameter is the type of some quantities
the library acts upon, keys in our case. It is also often the case
that the client wants to provide some of the operations used by the
library, here how to hash a key and how to compare elements. This
is a first step towards making libraries generic, so that they
implement the general functionalities of a data structure but let
the client choose specific details.
|
| Fri 27 Feb precept 7 |
Everything Hash to Go
In this precept, your group will gain practice working with hash
tables and related concepts.
|
| Tue 3 Mar |
No class (Spring break)
|
| Thu 5 Mar | |
| Fri 6 Mar |
| Tue 10 Mar Lecture 14 |
Generic Data Structures
In large (and not-so-large) systems, it is common to make multiple
uses of the same library, each instantiated with different
parameters. This is not possible in C0, however. To achieve this
goal, we look at a richer language, called C1. C1 provides two new
features: generic pointers and function pointers. Generic
pointers, void * in C, allow a same library type to be
instantiated with different client types at once, which gives us a
way to use a hash table library with both integers and strings as
keys for example. Function pointers allow a library to be
instantiated with different client-supplied operations in the same
program.
|
| Thu 12 Mar Lecture 15 |
Binary Search Trees
We discuss trees as an approach to representing a collection, and
binary search trees as an effective data structure to store and
operate on sorted data. In particular, most operations on balanced
binary search trees have a logarithm cost on the number of contained
data. Binary search trees can however become unbalanced over time.
|
| Fri 13 Mar precept 8 |
Generically Speaking
In this precept, your group will define and use a fully generic BST
dictionary library.
|
| Tue 24 Mar Lecture 18 |
C's Memory Model
C provides a very flexible view of memory, which allows writing
potentially dangerous code unless one is fully aware of the
consequences of one's decision. This lecture is about building this
awareness. We see that, while C overlays an organization on the
monolithic block of memory the operating systems assigns to a
running program, it also provides primitives that allow violating
this organization. We focus on two such primitives, pointer
arithmetic and address-of. While some uses are legitimate, others
are not. C's approach to many non-legitimate operations is to
declare them undefined, which means that what happens when a program
engages in them is decided by the specific implementation of the C
compiler in use.
|
| Thu 26 Mar Lecture 19 |
Types in C
In this lecture, we examine how C implements basic types, and what
we as programmers need to be aware of as we use them. We begin with
strings, that in C are just arrays of characters with
the null character playing a special role. A variety of
number types are available in C, but some of their characteristics
are not defined by the language, most notably their size and what
happens in case of overflow. As a consequence, different compilers
make different choices, which complicates writing code that will
work on generic hardware.
|
| Fri 27 Mar precept 10 |
C-ing is Believing
In this precept, your group will practice working with memory in C,
spotting undefined behavior, and casting numerical expressions.
|
| Mon 27 Apr (8:30-11:30am) final |
Final
|
| 2026 Iliano Cervesato |