Lab 1¶
Get the code¶
Use the curl
command to download the code you’ll need for the lab.
$ curl https://www.phy.olemiss.edu/~kbeach/phys540/src/lab1.tgz -O
$ tar xzf lab1.tgz
$ ls lab1
cpp/ julia/ python/ rust/
$ ls lab1/cpp
binomial.cpp makefile maxint.cpp
$ cd lab1/cpp
The instructions here focus on C++, but you may carry out the comparable steps in Julia, Python, or Rust. We’ll be exploring all of these languages in this course.
The file ending in .tgz
is an archived and compressed directory of
files (often called a tarball and sometimes bearing the suffix
.tar.gz
). The last command above moves you into the cpp
directory that is nested inside the lab1
directory.
Hello World!¶
Start by creating a file hello.cpp
that contains a “Hello World!”
program. See the C++ code listing below. Which text editor you use is
a matter of taste. The editors vim
, nano
, and emacs -nw
are
good options that operate within the terminal window. Although I like
vim
for some tasks, most students will likely prefer a modern editor
with a windowing interface, such as Atom,
Brackets, Sublime Text, TextMate;
a full-fledged IDE, such as XCode or Visual Studio Code; or a cloud-based notebook in the
style of IPhython or Jupyter.
#include <iostream>
using std::cout;
using std::endl;
int main()
{
char a = 'k';
cout << "He" << ++a << "lo World!" << endl;
return 0;
}
Compile it with g++
(or clang++
) and run the resulting
executable. You should be able to reproduce the following terminal
session:
$ g++ -o hello hello.cpp
$ ls -F
hello* hello.cpp
$ ./hello
Hello World!
a = 'k'
a = a+1
println("He",a,"lo World!")
a = 'k'
a = chr(ord(a)+1)
print("He"+a+"lo World!")
fn main() {
let mut a = 'k';
a = ((a as u8) + 1) as char;
println!("He{}lo World!",a);
}
All four programs produce identical output.
$ cd ../julia
$ julia hello.jl
Hello World!
$ cd ../python
$ python3 hello.py
Hello World!
$ cd ../rust
$ rustc hello.rs
$ ./hello
Hello World!
Look carefully at the program listings and take note of how the languages differ in their treatment of the character type and in their approach to formatting strings/streams for display on the screen. C++ and Julia both treat the character as an integer type and quietly cast between ordinals and characters as needed. Python and Rust both require some extra work to effect the conversion. For Rust, in particular, this is a matter of language philosophy. It is a very strongly typed language, and all type conversions are explicit.
Each of these languages has its own look and feel, but there are family
resemblances. C++ and Rust are traditional C-style “brace” languages,
with all code blocks enclosed in matching braces and each command
terminated by a semicolon. Layout is free-form, and extra white space is
ignored. (More precisely, any number of spaces, tabs, carriage returns
in sequence is equivalent to a single space.) The instructions to be
executed are enclosed in a function named main
.
Julia and Python each have the cleaner, more minimalistic feel of a scripting language. Semicolons are not needed as a separator. These languages assume one command per line. And braces aren’t used to organize the code. Instead, Python groups any commands that sit at a common level of indentation; Julia uses keyword … end blocks, in the style of ALGOL and Pascal.
Overflow¶
Compile and run the program maxint
. It generates the first six
powers of two, starting from the zeroth.
$ make maxint
g++ -o maxint maxint.cpp -O -ansi -pedantic -Wall
$ ./maxint
2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
Extend the program to determine the largest int
that can be
represented on your machine. Are there differences in behavior between
the C++, Julia, Python, and Rust implementations?
Be choosy¶
Included in the lab1 directory is a program that computes the (“\(\mathsf{m}\) choose \(\mathsf{k}\)”) binomial coefficient
The naive implementation
unsigned long int binomial(unsigned long int n, unsigned long int k)
{
return factorial(n)/factorial(k)/factorial(n-k);
}
seems to work fine for the combinatorics of a small number of items
$ make binomial
$ g++ -o binomial binomial.cpp -O -ansi -pedantic -Wall
$ ./binomial 8
(8 choose 0) = 1
(8 choose 1) = 8
(8 choose 2) = 28
(8 choose 3) = 56
(8 choose 4) = 70
(8 choose 5) = 56
(8 choose 6) = 28
(8 choose 7) = 8
(8 choose 8) = 1
but it fails for larger numbers
$ ./binomial 24 | head -n 10
(24 choose 0) = 1
(24 choose 1) = 1
(24 choose 2) = 0
(24 choose 3) = 0
(24 choose 4) = 0
(24 choose 5) = 0
(24 choose 6) = 2
(24 choose 7) = 5
(24 choose 8) = 12
(24 choose 9) = 22
Convince yourself that this calculational error is caused by integer overflow. Note that Python circumvents this by handling arbitrarily large numbers in software (rather than using fixed-bit-width integers in hardware, trading speed for flexibility).
Write a smart implementation of the binomial coefficient in any or all of C++, Julia, Python, and Rust based on the identity
where \(\mathsf{p} = \mathsf{\textsf{max}(k,n-k)}\) and \(\mathsf{q} = \mathsf{\textsf{min}(k,n-k)}\). You should be able to reproduce the following terminal session.
$ ./binomial 24 | head -n 10
(24 choose 0) = 1
(24 choose 1) = 24
(24 choose 2) = 276
(24 choose 3) = 2024
(24 choose 4) = 10626
(24 choose 5) = 42504
(24 choose 6) = 134596
(24 choose 7) = 346104
(24 choose 8) = 735471
(24 choose 9) = 1307504
Think about why this works to avoid overflow. Be sure that you can offer a careful and convincing explanation.