summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.devcontainer/devcontainer.json31
-rw-r--r--.gitea/issue_template.md5
-rw-r--r--.gitignore1
-rw-r--r--README.md6
-rw-r--r--build.zig110
-rw-r--r--exercises/033_iferror.zig2
-rw-r--r--exercises/046_optionals2.zig2
-rw-r--r--exercises/050_no_value.zig2
-rw-r--r--exercises/058_quiz7.zig2
-rw-r--r--exercises/060_floats.zig8
-rw-r--r--exercises/065_builtins2.zig20
-rw-r--r--exercises/071_comptime6.zig2
-rw-r--r--exercises/080_anonymous_structs.zig8
-rw-r--r--exercises/082_anonymous_structs3.zig6
-rw-r--r--exercises/097_bit_manipulation.zig8
-rw-r--r--exercises/099_formatting.zig4
-rw-r--r--exercises/100_for4.zig4
-rw-r--r--exercises/105_threading2.zig8
-rw-r--r--exercises/106_files.zig2
-rw-r--r--exercises/107_files2.zig8
-rw-r--r--exercises/108_labeled_switch.zig24
-rw-r--r--exercises/109_vectors.zig147
-rw-r--r--exercises/110_quiz9.zig484
-rwxr-xr-xpatches/eowyn.sh14
-rw-r--r--patches/patches/046_optionals2.patch4
-rw-r--r--patches/patches/058_quiz7.patch6
-rw-r--r--patches/patches/060_floats.patch12
-rw-r--r--patches/patches/065_builtins2.patch10
-rw-r--r--patches/patches/082_anonymous_structs3.patch6
-rw-r--r--patches/patches/097_bit_manipulation.patch6
-rw-r--r--patches/patches/099_formatting.patch4
-rw-r--r--patches/patches/100_for4.patch2
-rw-r--r--patches/patches/106_files.patch91
-rw-r--r--patches/patches/107_files2.patch54
-rw-r--r--patches/patches/109_vectors.patch13
-rw-r--r--patches/patches/110_quiz9.patch56
36 files changed, 933 insertions, 239 deletions
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index beea353..0000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,31 +0,0 @@
-// For format details, see https://aka.ms/devcontainer.json. For config options, see the
-// README at: https://github.com/devcontainers/templates/tree/main/src/debian
-{
- "name": "Ziglings",
- // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
- "image": "mcr.microsoft.com/devcontainers/base:bullseye",
- "features": {
- "ghcr.io/devcontainers-contrib/features/zig:1": {
- "version": "master"
- }
- },
- "customizations": {
- "vscode": {
- "extensions": [
- "ziglang.vscode-zig"
- ]
- }
- }
-
- // Features to add to the dev container. More info: https://containers.dev/features.
- // "features": {},
-
- // Use 'forwardPorts' to make a list of ports inside the container available locally.
- // "forwardPorts": [],
-
- // Configure tool-specific properties.
- // "customizations": {},
-
- // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
- // "remoteUser": "root"
-}
diff --git a/.gitea/issue_template.md b/.gitea/issue_template.md
new file mode 100644
index 0000000..4af39e3
--- /dev/null
+++ b/.gitea/issue_template.md
@@ -0,0 +1,5 @@
+Ziglings is a progressive learning series — each exercise builds on previous ones.
+Before opening an issue, please ensure you've followed the path and read the instructions carefully.
+
+Respectful and constructive feedback is always welcome.
+
diff --git a/.gitignore b/.gitignore
index 53a6184..0fa4230 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
/answers/
/patches/healed/
/output/
+.progress.txt
# Leave this in here for older zig versions
/zig-cache/
diff --git a/README.md b/README.md
index 63d8dbf..a6e760f 100644
--- a/README.md
+++ b/README.md
@@ -173,6 +173,11 @@ zig build -Dn=19 -l
...
```
+To reset the progress (have it run all the exercises that have already been completed):
+```
+zig build -Dreset
+```
+
## What's Covered
The primary goal for Ziglings is to cover the core Zig language.
@@ -228,6 +233,7 @@ Zig Core Language
* [X] Interfaces
* [X] Bit manipulation
* [X] Working with C
+* [ ] Opaque types (anyopaque)
* [X] Threading
* [x] Labeled switch
* [x] Vector operations (SIMD)
diff --git a/build.zig b/build.zig
index a1f6029..6c76917 100644
--- a/build.zig
+++ b/build.zig
@@ -120,6 +120,8 @@ pub const logo =
\\
;
+const progress_filename = ".progress.txt";
+
pub fn build(b: *Build) !void {
if (!validate_exercises()) std.process.exit(2);
@@ -162,6 +164,7 @@ pub fn build(b: *Build) !void {
const exno: ?usize = b.option(usize, "n", "Select exercise");
const rand: ?bool = b.option(bool, "random", "Select random exercise");
const start: ?usize = b.option(usize, "s", "Start at exercise");
+ const reset: ?bool = b.option(bool, "reset", "Reset exercise progress");
const sep = std.fs.path.sep_str;
const healed_path = if (override_healed_path) |path|
@@ -197,7 +200,7 @@ pub fn build(b: *Build) !void {
if (rand) |_| {
// Random build mode: verifies one random exercise.
- // like for 'exno' but chooses a random exersise number.
+ // like for 'exno' but chooses a random exercise number.
print("work in progress: check a random exercise\n", .{});
var prng = std.Random.DefaultPrng.init(blk: {
@@ -242,17 +245,61 @@ pub fn build(b: *Build) !void {
return;
}
+ if (reset) |_| {
+ std.fs.cwd().deleteFile(progress_filename) catch |err| {
+ switch (err) {
+ std.fs.Dir.DeleteFileError.FileNotFound => {},
+ else => {
+ print("Unable to remove progress file, Error: {}\n", .{err});
+ return err;
+ },
+ }
+ };
+
+ print("Progress reset, {s} removed.\n", .{progress_filename});
+ std.process.exit(0);
+ }
+
// Normal build mode: verifies all exercises according to the recommended
// order.
const ziglings_step = b.step("ziglings", "Check all ziglings");
b.default_step = ziglings_step;
var prev_step = &header_step.step;
+
+ var starting_exercise: u32 = 0;
+
+ if (std.fs.cwd().openFile(progress_filename, .{})) |progress_file| {
+ defer progress_file.close();
+
+ const progress_file_size = try progress_file.getEndPos();
+
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ defer _ = gpa.deinit();
+ const allocator = gpa.allocator();
+ const contents = try progress_file.readToEndAlloc(allocator, progress_file_size);
+ defer allocator.free(contents);
+
+ starting_exercise = try std.fmt.parseInt(u32, contents, 10);
+ } else |err| {
+ switch (err) {
+ std.fs.File.OpenError.FileNotFound => {
+ // This is fine, may be the first time tests are run or progress have been reset
+ },
+ else => {
+ print("Unable to open {s}: {}\n", .{ progress_filename, err });
+ return err;
+ },
+ }
+ }
+
for (exercises) |ex| {
- const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
- verify_stepn.step.dependOn(prev_step);
+ if (starting_exercise < ex.number()) {
+ const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
+ verify_stepn.step.dependOn(prev_step);
- prev_step = &verify_stepn.step;
+ prev_step = &verify_stepn.step;
+ }
}
ziglings_step.dependOn(prev_step);
@@ -403,6 +450,18 @@ const ZiglingStep = struct {
, .{ red, reset, exercise_output, red, reset, output, red, reset });
}
+ const progress = try std.fmt.allocPrint(b.allocator, "{d}", .{self.exercise.number()});
+ defer b.allocator.free(progress);
+
+ const file = try std.fs.cwd().createFile(
+ progress_filename,
+ .{ .read = true, .truncate = true },
+ );
+ defer file.close();
+
+ try file.writeAll(progress);
+ try file.sync();
+
print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
}
@@ -904,7 +963,7 @@ const exercises = [_]Exercise{
},
.{
.main_file = "060_floats.zig",
- .output = "Shuttle liftoff weight: 2032092kg",
+ .output = "Shuttle liftoff weight: 2032 metric tons",
},
.{
.main_file = "061_coercions.zig",
@@ -970,6 +1029,7 @@ const exercises = [_]Exercise{
.{
.main_file = "074_comptime9.zig",
.output = "My llama value is 2.",
+ .skip = true,
},
.{
.main_file = "075_quiz8.zig",
@@ -1086,7 +1146,7 @@ const exercises = [_]Exercise{
},
.{
.main_file = "097_bit_manipulation.zig",
- .output = "x = 0; y = 1",
+ .output = "x = 1011; y = 1101",
},
.{
.main_file = "098_bit_manipulation2.zig",
@@ -1202,6 +1262,44 @@ const exercises = [_]Exercise{
.output = "The pull request has been merged.",
},
.{
+ .main_file = "109_vectors.zig",
+ .output =
+ \\Max difference (old fn): 0.014
+ \\Max difference (new fn): 0.014
+ ,
+ },
+ .{ .main_file = "110_quiz9.zig", .output =
+ \\Toggle pins with XOR on PORTB
+ \\-----------------------------
+ \\ 1100 // (initial state of PORTB)
+ \\^ 0101 // (bitmask)
+ \\= 1001
+ \\
+ \\ 1100 // (initial state of PORTB)
+ \\^ 0011 // (bitmask)
+ \\= 1111
+ \\
+ \\Set pins with OR on PORTB
+ \\-------------------------
+ \\ 1001 // (initial state of PORTB)
+ \\| 0100 // (bitmask)
+ \\= 1101
+ \\
+ \\ 1001 // (reset state)
+ \\| 0100 // (bitmask)
+ \\= 1101
+ \\
+ \\Clear pins with AND and NOT on PORTB
+ \\------------------------------------
+ \\ 1110 // (initial state of PORTB)
+ \\& 1011 // (bitmask)
+ \\= 1010
+ \\
+ \\ 0111 // (reset state)
+ \\& 1110 // (bitmask)
+ \\= 0110
+ },
+ .{
.main_file = "999_the_end.zig",
.output =
\\
diff --git a/exercises/033_iferror.zig b/exercises/033_iferror.zig
index 6ba0c61..12da2d2 100644
--- a/exercises/033_iferror.zig
+++ b/exercises/033_iferror.zig
@@ -17,7 +17,7 @@
//
// if (foo) |value| {
// ...
-// } else |err| switch(err) {
+// } else |err| switch (err) {
// ...
// }
//
diff --git a/exercises/046_optionals2.zig b/exercises/046_optionals2.zig
index a5436d9..b5fffbb 100644
--- a/exercises/046_optionals2.zig
+++ b/exercises/046_optionals2.zig
@@ -48,7 +48,7 @@ pub fn main() void {
// If e1 and e2 are valid pointers to elephants,
// this function links the elephants so that e1's tail "points" to e2.
fn linkElephants(e1: ?*Elephant, e2: ?*Elephant) void {
- e1.?.*.tail = e2.?;
+ e1.?.tail = e2.?;
}
// This function visits all elephants once, starting with the
diff --git a/exercises/050_no_value.zig b/exercises/050_no_value.zig
index 8c73ed3..f5365cf 100644
--- a/exercises/050_no_value.zig
+++ b/exercises/050_no_value.zig
@@ -43,7 +43,7 @@
//
// "void" is a _type_, not a value. It is the most popular of the
// Zero Bit Types (those types which take up absolutely no space
-// and have only a semantic value. When compiled to executable
+// and have only a semantic value). When compiled to executable
// code, zero bit types generate no code at all. The above example
// shows a variable foo of type void which is assigned the value
// of an empty expression. It's much more common to see void as
diff --git a/exercises/058_quiz7.zig b/exercises/058_quiz7.zig
index cf32fc3..fda83fc 100644
--- a/exercises/058_quiz7.zig
+++ b/exercises/058_quiz7.zig
@@ -190,7 +190,7 @@ const TripItem = union(enum) {
fn printMe(self: TripItem) void {
switch (self) {
// Oops! The hermit forgot how to capture the union values
- // in a switch statement. Please capture both values as
+ // in a switch statement. Please capture each value as
// 'p' so the print statements work!
.place => print("{s}", .{p.name}),
.path => print("--{}->", .{p.dist}),
diff --git a/exercises/060_floats.zig b/exercises/060_floats.zig
index 6f341ad..b570518 100644
--- a/exercises/060_floats.zig
+++ b/exercises/060_floats.zig
@@ -41,14 +41,14 @@ pub fn main() void {
// The approximate weight of the Space Shuttle upon liftoff
// (including boosters and fuel tank) was 4,480,000 lb.
//
- // We'll convert this weight from pound to kilograms at a
- // conversion of 0.453592kg to the pound.
- const shuttle_weight: f16 = 0.453592 * 4480e6;
+ // We'll convert this weight from pounds to metric units at a
+ // conversion of 0.453592 kg to the pound.
+ const shuttle_weight: f16 = 0.453592 * 4480e3;
// By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see
// how decimal formatting works.
- print("Shuttle liftoff weight: {d:.0}kg\n", .{shuttle_weight});
+ print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight});
}
// Floating further:
diff --git a/exercises/065_builtins2.zig b/exercises/065_builtins2.zig
index 0790db4..6b8168c 100644
--- a/exercises/065_builtins2.zig
+++ b/exercises/065_builtins2.zig
@@ -110,15 +110,15 @@ pub fn main() void {
// name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!):
if (fields[0].??? != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[0].name});
+ print(" {s}", .{fields[0].name});
}
if (fields[1].??? != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[1].name});
+ print(" {s}", .{fields[1].name});
}
if (fields[2].??? != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[2].name});
+ print(" {s}", .{fields[2].name});
}
// Yuck, look at all that repeated code above! I don't know
@@ -136,14 +136,16 @@ pub fn main() void {
// But a change after Zig 0.10.0 added the source file name to the
// type. "Narcissus" became "065_builtins2.Narcissus".
//
-// To fix this, I've added this function to strip the filename from
-// the front of the type name in the dumbest way possible. (It returns
-// a slice of the type name starting at character 14 (assuming
-// single-byte characters).
+// To fix this, we've added this function to strip the filename from
+// the front of the type name. (It returns a slice of the type name
+// starting at the index + 1 of character ".")
//
// We'll be seeing @typeName again in Exercise 070. For now, you can
// see that it takes a Type and returns a u8 "string".
fn maximumNarcissism(myType: anytype) []const u8 {
- // Turn '065_builtins2.Narcissus' into 'Narcissus'
- return @typeName(myType)[14..];
+ const indexOf = @import("std").mem.indexOf;
+
+ // Turn "065_builtins2.Narcissus" into "Narcissus"
+ const name = @typeName(myType);
+ return name[indexOf(u8, name, ".").? + 1 ..];
}
diff --git a/exercises/071_comptime6.zig b/exercises/071_comptime6.zig
index 5938abc..9b3e5a2 100644
--- a/exercises/071_comptime6.zig
+++ b/exercises/071_comptime6.zig
@@ -7,7 +7,7 @@
// doing this work.
//
// An 'inline for' is performed at compile time, allowing you to
-// programatically loop through a series of items in situations
+// programmatically loop through a series of items in situations
// like those mentioned above where a regular runtime 'for' loop
// wouldn't be allowed:
//
diff --git a/exercises/080_anonymous_structs.zig b/exercises/080_anonymous_structs.zig
index 55dedd4..4e3ce84 100644
--- a/exercises/080_anonymous_structs.zig
+++ b/exercises/080_anonymous_structs.zig
@@ -19,12 +19,12 @@
// const MyBar = Bar(); // store the struct type
// const bar = Bar() {}; // create instance of the struct
//
-// * The value of @typeName(Bar()) is "Bar()".
-// * The value of @typeName(MyBar) is "Bar()".
-// * The value of @typeName(@TypeOf(bar)) is "Bar()".
+// * The value of @typeName(Bar()) is "<filename>.Bar()".
+// * The value of @typeName(MyBar) is "<filename>.Bar()".
+// * The value of @typeName(@TypeOf(bar)) is "<filename>.Bar()".
//
// You can also have completely anonymous structs. The value
-// of @typeName(struct {}) is "struct:<position in source>".
+// of @typeName(struct {}) is "<filename>.<function>__struct_<nnn>".
//
const print = @import("std").debug.print;
diff --git a/exercises/082_anonymous_structs3.zig b/exercises/082_anonymous_structs3.zig
index 573d0bb..469cd66 100644
--- a/exercises/082_anonymous_structs3.zig
+++ b/exercises/082_anonymous_structs3.zig
@@ -95,13 +95,15 @@ fn printTuple(tuple: anytype) void {
// Each 'field' in this loop is one of these:
//
// pub const StructField = struct {
- // name: []const u8,
+ // name: [:0]const u8,
// type: type,
- // default_value: anytype,
+ // default_value_ptr: ?*const anyopaque,
// is_comptime: bool,
// alignment: comptime_int,
// };
//
+ // Note we will learn about 'anyopaque' type later
+ //
// You'll need this builtin:
//
// @field(lhs: anytype, comptime field_name: []const u8)
diff --git a/exercises/097_bit_manipulation.zig b/exercises/097_bit_manipulation.zig
index 03fc72d..0e64ad7 100644
--- a/exercises/097_bit_manipulation.zig
+++ b/exercises/097_bit_manipulation.zig
@@ -71,9 +71,9 @@ const print = std.debug.print;
pub fn main() !void {
- // As in the example above, we use 1 and 0 as values for x and y
- var x: u8 = 1;
- var y: u8 = 0;
+ // Let us use 1101 and 1011 as values for x and y
+ var x: u8 = 0b1101;
+ var y: u8 = 0b1011;
// Now we swap the values of the two variables by doing xor on them
x ^= y;
@@ -82,7 +82,7 @@ pub fn main() !void {
// What must be written here?
???;
- print("x = {d}; y = {d}\n", .{ x, y });
+ print("x = {b}; y = {b}\n", .{ x, y });
}
// This variable swap takes advantage of the fact that the value resulting
diff --git a/exercises/099_formatting.zig b/exercises/099_formatting.zig
index 4b64209..ef6b84e 100644
--- a/exercises/099_formatting.zig
+++ b/exercises/099_formatting.zig
@@ -16,7 +16,7 @@
// Therefore, the comments for the format() function are the only
// way to definitively learn how to format strings in Zig:
//
-// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L29
+// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L33
//
// Zig already has a very nice selection of formatting options.
// These can be used in different ways, but generally to convert
@@ -60,7 +60,7 @@
// variety of formatting instructions. It's basically a tiny
// language of its own. Here's a numeric example:
//
-// print("Catch-{x:0>4}.", .{twenty_two});
+// print("Catch-0x{x:0>4}.", .{twenty_two});
//
// This formatting instruction outputs a hexadecimal number with
// leading zeros:
diff --git a/exercises/100_for4.zig b/exercises/100_for4.zig
index e0fa602..c8a1161 100644
--- a/exercises/100_for4.zig
+++ b/exercises/100_for4.zig
@@ -41,12 +41,12 @@ pub fn main() void {
for (hex_nums, ???) |hn, ???| {
if (hn != dn) {
- std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
+ print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return;
}
}
- std.debug.print("Arrays match!\n", .{});
+ print("Arrays match!\n", .{});
}
//
// You are perhaps wondering what happens if one of the two lists
diff --git a/exercises/105_threading2.zig b/exercises/105_threading2.zig
index 7ca8f5c..374391a 100644
--- a/exercises/105_threading2.zig
+++ b/exercises/105_threading2.zig
@@ -21,9 +21,9 @@
// There were the Scottish mathematician Gregory and the German
// mathematician Leibniz, and even a few hundred years earlier the Indian
// mathematician Madhava. All of them independently developed the same
-// formula, which was published by Leibnitz in 1682 in the journal
+// formula, which was published by Leibniz in 1682 in the journal
// "Acta Eruditorum".
-// This is why this method has become known as the "Leibnitz series",
+// This is why this method has become known as the "Leibniz series",
// although the other names are also often used today.
// We will not go into the formula and its derivation in detail, but
// will deal with the series straight away:
@@ -39,7 +39,7 @@
// in practice. Because either you don't need the precision, or you use a
// calculator in which the number is stored as a very precise constant.
// But at some point this constant was calculated and we are doing the same
-// now.The question at this point is, how many partial values do we have
+// now. The question at this point is, how many partial values do we have
// to calculate for which accuracy?
//
// The answer is chewing, to get 8 digits after the decimal point we need
@@ -50,7 +50,7 @@
// enough for us for now, because we want to understand the principle and
// nothing more, right?
//
-// As we have already discovered, the Leibnitz series is a series with a
+// As we have already discovered, the Leibniz series is a series with a
// fixed distance of 2 between the individual partial values. This makes
// it easy to apply a simple loop to it, because if we start with n = 1
// (which is not necessarily useful now) we always have to add 2 in each
diff --git a/exercises/106_files.zig b/exercises/106_files.zig
index f5fd1ac..b224508 100644
--- a/exercises/106_files.zig
+++ b/exercises/106_files.zig
@@ -9,7 +9,7 @@
// by organizing them into directories, which hold files and other directories,
// thus creating a tree structure that can be navigated.
//
-// Fortunately, the Zig standard library provides a simple API for interacting
+// Fortunately, the Zig Standard Library provides a simple API for interacting
// with the file system, see the detail documentation here:
//
// https://ziglang.org/documentation/master/std/#std.fs
diff --git a/exercises/107_files2.zig b/exercises/107_files2.zig
index 45e12f5..d059879 100644
--- a/exercises/107_files2.zig
+++ b/exercises/107_files2.zig
@@ -12,7 +12,7 @@
// Alright, bud, lean in close. Here's the game plan.
// - First, we open the {project_root}/output/ directory
// - Secondly, we open file `zigling.txt` in that directory
-// - Then, we initalize an array of characters with all letter 'A', and print it
+// - Then, we initialize an array of characters with all letter 'A', and print it
// - After that, we read the content of the file into the array
// - Finally, we print out the content we just read
@@ -30,9 +30,9 @@ pub fn main() !void {
const file = try output_dir.openFile("zigling.txt", .{});
defer file.close();
- // initalize an array of u8 with all letter 'A'
+ // initialize an array of u8 with all letter 'A'
// we need to pick the size of the array, 64 seems like a good number
- // fix the initalization below
+ // fix the initialization below
var content = ['A']*64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
@@ -40,7 +40,7 @@ pub fn main() !void {
// okay, seems like a threat of violence is not the answer in this case
// can you go here to find a way to read the content?
// https://ziglang.org/documentation/master/std/#std.fs.File
- // hint: you might find two answers that are both vaild in this case
+ // hint: you might find two answers that are both valid in this case
const bytes_read = zig_read_the_file_or_i_will_fight_you(&content);
// Woah, too screamy. I know you're excited for zigling time but tone it down a bit.
diff --git a/exercises/108_labeled_switch.zig b/exercises/108_labeled_switch.zig
index 88cb196..897fcf5 100644
--- a/exercises/108_labeled_switch.zig
+++ b/exercises/108_labeled_switch.zig
@@ -15,16 +15,16 @@
// 1 => { op = 2; continue; },
// 2 => { op = 3; continue; },
// 3 => return,
-// 4 => {},
+// else => {},
// }
// break;
// }
-// std.debug.print("This statement cannot be reached");
+// std.debug.print("This statement cannot be reached\n", .{});
// }
//
-// By combining all we've learned so far, we can now proceed with a labeled switch
+// By combining all we've learned so far, we can now proceed with a labeled switch.
//
-// A labeled switch is some extra syntatic sugar, which comes with all sorts of
+// A labeled switch is some extra syntactic sugar, which comes with all sorts of
// candy (performance benefits). Don't believe me? Directly to source https://github.com/ziglang/zig/pull/21367
//
// Here is the previous excerpt implemented as a labeled switch instead:
@@ -34,21 +34,23 @@
// 1 => continue :foo 2,
// 2 => continue :foo 3,
// 3 => return,
-// 4 => {},
+// else => {},
// }
-// std.debug.print("This statement cannot be reached");
+// std.debug.print("This statement cannot be reached\n", .{});
// }
//
// The flow of execution on this second case is:
// 1. The switch starts with value '1';
// 2. The switch evaluates to case '1' which in turn uses the continue statement
-// to re-evaluate the labeled switch again, now providing the value '2';
+// to re-evaluate the labeled switch again, now providing the value '2';
// 3. In the case '2' we repeat the same pattern as case '1'
-// but instead the value to be evaluated is now '3';
-// 4. Finally we get to case '3', where we return from the function as a whole.
+// but instead the value to be evaluated is now '3';
+// 4. Finally we get to case '3', where we return from the function as a whole,
+// so the debug statement is never executed.
+// 5. In this example, since the input does not have clear, exhaustive patterns and
+// can essentially be any 'u8' integer, we need to handle all cases not explicitly
+// covered by using the 'else => {}' branch as the default case.
//
-// Since step 4 or a break stament do not exist in this switch, the debug statement is
-// never executed
//
const std = @import("std");
diff --git a/exercises/109_vectors.zig b/exercises/109_vectors.zig
new file mode 100644
index 0000000..96892ca
--- /dev/null
+++ b/exercises/109_vectors.zig
@@ -0,0 +1,147 @@
+// So far in Ziglings, we've seen how for loops can be used to
+// repeat calculations across an array in several ways.
+//
+// For loops are generally great for this kind of task, but
+// sometimes they don't fully utilize the capabilities of the
+// CPU.
+//
+// Most modern CPUs can execute instructions in which SEVERAL
+// calculations are performed WITHIN registers at the SAME TIME.
+// These are known as "single instruction, multiple data" (SIMD)
+// instructions. SIMD instructions can make code significantly
+// more performant.
+//
+// To see why, imagine we have a program in which we take the
+// square root of four (changing) f32 floats.
+//
+// A simple compiler would take the program and produce machine code
+// which calculates each square root sequentially. Most registers on
+// modern CPUs have 64 bits, so we could imagine that each float moves
+// into a 64-bit register, and the following happens four times:
+//
+// 32 bits 32 bits
+// +-------------------+
+// register | 0 | x |
+// +-------------------+
+//
+// |
+// [SQRT instruction]
+// V
+//
+// +-------------------+
+// | 0 | sqrt(x) |
+// +-------------------+
+//
+// Notice that half of the register contains blank data to which
+// nothing happened. What a waste! What if we were able to use
+// that space instead? This is the idea at the core of SIMD.
+//
+// Most modern CPUs contain specialized registers with at least 128 bits
+// for performing SIMD instructions. On a machine with 128-bit SIMD
+// registers, a smart compiler would probably NOT issue four sqrt
+// instructions as above, but instead pack the floats into a single
+// 128-bit register, then execute a single "packed" sqrt
+// instruction to do ALL the square root calculations at once.
+//
+// For example:
+//
+//
+// 32 bits 32 bits 32 bits 32 bits
+// +---------------------------------------+
+// register | 4.0 | 9.0 | 25.0 | 49.0 |
+// +---------------------------------------+
+//
+// |
+// [SIMD SQRT instruction]
+// V
+//
+// +---------------------------------------+
+// register | 2.0 | 3.0 | 5.0 | 7.0 |
+// +---------------------------------------+
+//
+// Pretty cool, right?
+//
+// Code with SIMD instructions is usually more performant than code
+// without SIMD instructions. Zig cares a lot about performance,
+// so it has built-in support for SIMD! It has a data structure that
+// directly supports SIMD instructions:
+//
+// +-----------+
+// | Vectors |
+// +-----------+
+//
+// Operations performed on vectors in Zig will be done in parallel using
+// SIMD instructions, whenever possible.
+//
+// Defining vectors in Zig is straightforwards. No library import is needed.
+const v1 = @Vector(3, i32){ 1, 10, 100 };
+const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
+
+// Vectors support the same builtin operators as their underlying base types.
+const v3 = v1 + v1; // { 2, 20, 200};
+const v4 = v2 * v2; // { 4.0, 9.0, 25.0};
+
+// Intrinsics that apply to base types usually extend to vectors.
+const v5: @Vector(3, f32) = @floatFromInt(v3); // { 2.0, 20.0, 200.0}
+const v6 = v4 - v5; // { 2.0, -11.0, -175.0}
+const v7 = @abs(v6); // { 2.0, 11.0, 175.0}
+
+// We can make constant vectors, and reduce vectors.
+const v8: @Vector(4, u8) = @splat(2); // { 2, 2, 2, 2}
+const v8_sum = @reduce(.Add, v8); // 8
+const v8_min = @reduce(.Min, v8); // 2
+
+// Fixed-length arrays can be automatically assigned to vectors (and vice-versa).
+const single_digit_primes = [4]i8{ 2, 3, 5, 7 };
+const prime_vector: @Vector(4, i8) = single_digit_primes;
+
+// Now let's use vectors to simplify and optimize some code!
+//
+// Ewa is writing a program in which they frequently want to compare
+// two lists of four f32s. Ewa expects the lists to be similar, and
+// wants to determine the largest pairwise difference between the lists.
+//
+// Ewa wrote the following function to figure this out.
+
+fn calcMaxPairwiseDiffOld(list1: [4]f32, list2: [4]f32) f32 {
+ var max_diff: f32 = 0;
+ for (list1, list2) |n1, n2| {
+ const abs_diff = @abs(n1 - n2);
+ if (abs_diff > max_diff) {
+ max_diff = abs_diff;
+ }
+ }
+ return max_diff;
+}
+
+// Ewa heard about vectors in Zig, and started writing a new vector
+// version of the function, but has got stuck!
+//
+// Help Ewa finish the vector version! The examples above should help.
+
+const Vec4 = @Vector(4, f32);
+fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
+ const abs_diff_vec = ???;
+ const max_diff = @reduce(???, abs_diff_vec);
+ return max_diff;
+}
+
+// Quite the simplification! We could even write the function in one line
+// and it would still be readable.
+//
+// Since the entire function is now expressed in terms of vector operations,
+// the Zig compiler will easily be able to compile it down to machine code
+// which utilizes the all-powerful SIMD instructions and does a lot of the
+// computation in parallel.
+
+const std = @import("std");
+const print = std.debug.print;
+
+pub fn main() void {
+ const l1 = [4]f32{ 3.141, 2.718, 0.577, 1.000 };
+ const l2 = [4]f32{ 3.154, 2.707, 0.591, 0.993 };
+ const mpd_old = calcMaxPairwiseDiffOld(l1, l2);
+ const mpd_new = calcMaxPairwiseDiffNew(l1, l2);
+ print("Max difference (old fn): {d: >5.3}\n", .{mpd_old});
+ print("Max difference (new fn): {d: >5.3}\n", .{mpd_new});
+}
diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig
new file mode 100644
index 0000000..8f5cb61
--- /dev/null
+++ b/exercises/110_quiz9.zig
@@ -0,0 +1,484 @@
+// ----------------------------------------------------------------------------
+// Quiz Time: Toggling, Setting, and Clearing Bits
+// ----------------------------------------------------------------------------
+//
+// Another exciting thing about Zig is its suitability for embedded
+// programming. Your Zig code doesn't have to remain on your laptop; you can
+// also deploy your code to microcontrollers! This means you can write Zig to
+// drive your next robot or greenhouse climate control system! Ready to enter
+// the exciting world of embedded programming? Let's get started!
+//
+// ----------------------------------------------------------------------------
+// Some Background
+// ----------------------------------------------------------------------------
+//
+// A common activity in microcontroller programming is setting and clearing
+// bits on input and output pins. This lets you control LEDs, sensors, motors
+// and more! In a previous exercise (097_bit_manipulation.zig) you learned how
+// to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will
+// test your knowledge of bit manipulation in Zig while giving you a taste of
+// what it's like to control registers in a real microcontroller. Included at
+// the end are some helper functions that demonstrate how we might make our
+// code a little more readable.
+//
+// Below is a pinout diagram for the famous ATmega328 AVR microcontroller used
+// as the primary microchip on popular microcontroller platforms like the
+// Arduino UNO.
+//
+// ============ PINOUT DIAGRAM FOR ATMEGA328 MICROCONTROLLER ============
+// _____ _____
+// | U |
+// (RESET) PC6 --| 1 28 |-- PC5
+// PD0 --| 2 27 |-- PC4
+// PD1 --| 3 26 |-- PC3
+// PD2 --| 4 25 |-- PC2
+// PD3 --| 5 24 |-- PC1
+// PD4 --| 6 23 |-- PC0
+// VCC --| 7 22 |-- GND
+// GND --| 8 21 |-- AREF
+// |-- PB6 --| 9 20 |-- AVCC
+// |-- PB7 --| 10 19 |-- PB5 --|
+// | PD5 --| 11 18 |-- PB4 --|
+// | PD6 --| 12 17 |-- PB3 --|
+// | PD7 --| 13 16 |-- PB2 --|
+// |-- PB0 --| 14 15 |-- PB1 --|
+// | |___________| |
+// \_______________________________/
+// |
+// PORTB
+//
+// Drawing inspiration from this diagram, we'll use the pins for PORTB as our
+// mental model for this quiz on bit manipulation. It should be noted that
+// in the following problems we are using ordinary variables, one of which we
+// have named PORTB, to simulate modifying the bits of real hardware registers.
+// But in actual microcontroller code, PORTB would be defined something like
+// this:
+// pub const PORTB = @as(*volatile u8, @ptrFromInt(0x25));
+//
+// This lets the compiler know not to make any optimizations to PORTB so that
+// the IO pins are properly mapped to our code.
+//
+// NOTE : To keep things simple, the following problems are given using type
+// u4, so applying the output to PORTB would only affect the lower four pins
+// PB0..PB3. Of course, there is nothing to prevent you from swapping the u4
+// with a u8 so you can control all 8 of PORTB's IO pins.
+
+const std = @import("std");
+const print = std.debug.print;
+const testing = std.testing;
+
+pub fn main() !void {
+ var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity
+
+ // ------------------------------------------------------------------------
+ // Quiz
+ // ------------------------------------------------------------------------
+
+ // See if you can solve the following problems. The last two problems throw
+ // you a bit of a curve ball. Try solving them on your own. If you need
+ // help, scroll to the bottom of main to see some in depth explanations on
+ // toggling, setting, and clearing bits in Zig.
+
+ print("Toggle pins with XOR on PORTB\n", .{});
+ print("-----------------------------\n", .{});
+ PORTB = 0b1100;
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("^ {b:0>4} // (bitmask)\n", .{0b0101});
+ PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
+ checkAnswer(0b1001, PORTB);
+
+ newline();
+
+ PORTB = 0b1100;
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("^ {b:0>4} // (bitmask)\n", .{0b0011});
+ PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
+ checkAnswer(0b1111, PORTB);
+
+ newline();
+
+ print("Set pins with OR on PORTB\n", .{});
+ print("-------------------------\n", .{});
+
+ PORTB = 0b1001; // reset PORTB
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("| {b:0>4} // (bitmask)\n", .{0b0100});
+ PORTB = PORTB ??? (1 << 2); // What's missing here?
+ checkAnswer(0b1101, PORTB);
+
+ newline();
+
+ PORTB = 0b1001; // reset PORTB
+ print(" {b:0>4} // (reset state)\n", .{PORTB});
+ print("| {b:0>4} // (bitmask)\n", .{0b0100});
+ PORTB ??? (1 << 2); // What's missing here?
+ checkAnswer(0b1101, PORTB);
+
+ newline();
+
+ print("Clear pins with AND and NOT on PORTB\n", .{});
+ print("------------------------------------\n", .{});
+
+ PORTB = 0b1110; // reset PORTB
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("& {b:0>4} // (bitmask)\n", .{0b1011});
+ PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
+ checkAnswer(0b1010, PORTB);
+
+ newline();
+
+ PORTB = 0b0111; // reset PORTB
+ print(" {b:0>4} // (reset state)\n", .{PORTB});
+ print("& {b:0>4} // (bitmask)\n", .{0b1110});
+ PORTB &= ~(1 << 0); // What's missing here?
+ checkAnswer(0b0110, PORTB);
+
+ newline();
+ newline();
+}
+
+// ************************************************************************
+// IN-DEPTH EXPLANATIONS BELOW
+// ************************************************************************
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+// ------------------------------------------------------------------------
+// Toggling bits with XOR:
+// ------------------------------------------------------------------------
+// XOR stands for "exclusive or". We can toggle bits with the ^ (XOR)
+// bitwise operator, like so:
+//
+//
+// In order to output a 1, the logic of an XOR operation requires that the
+// two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will
+// both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior
+// of outputting a 0 when both inputs are 1s is what makes it different from
+// the OR operator; it also gives us the ability to toggle bits by putting
+// 1s into our bitmask.
+//
+// - 1s in our bitmask operand, can be thought of as causing the
+// corresponding bits in the other operand to flip to the opposite value.
+// - 0s cause no change.
+//
+// The 0s in our bitmask preserve these values
+// -XOR op- ---expanded--- in the output.
+// _______________/
+// / /
+// 1100 1 1 0 0
+// ^ 0101 0 1 0 1 (bitmask)
+// ------ - - - -
+// = 1001 1 0 0 1 <- This bit was already cleared.
+// \_______\
+// \
+// We can think of these bits having flipped
+// because of the presence of 1s in those columns
+// of our bitmask.
+//
+// Now let's take a look at setting bits with the | operator.
+//
+//
+//
+//
+//
+// ------------------------------------------------------------------------
+// Setting bits with OR:
+// ------------------------------------------------------------------------
+// We can set bits on PORTB with the | (OR) operator, like so:
+//
+// var PORTB: u4 = 0b1001;
+// PORTB = PORTB | 0b0010;
+// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
+//
+// -OR op- ---expanded---
+// _ Set only this bit.
+// /
+// 1001 1 0 0 1
+// | 0010 0 0 1 0 (bitmask)
+// ------ - - - -
+// = 1011 1 0 1 1
+// \___\_______\
+// \
+// These bits remain untouched because OR-ing with
+// a 0 effects no change.
+//
+// ------------------------------------------------------------------------
+// To create a bitmask like 0b0010 used above:
+//
+// 1. First, shift the value 1 over one place with the bitwise << (shift
+// left) operator as indicated below:
+// 1 << 0 -> 0001
+// 1 << 1 -> 0010 <-- Shift 1 one place to the left
+// 1 << 2 -> 0100
+// 1 << 3 -> 1000
+//
+// This allows us to rewrite the above code like this:
+//
+// var PORTB: u4 = 0b1001;
+// PORTB = PORTB | (1 << 1);
+// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
+//
+// Finally, as in the C language, Zig allows us to use the |= operator, so
+// we can rewrite our code again in an even more compact and idiomatic
+// form: PORTB |= (1 << 1)
+
+// So now we've covered how to toggle and set bits. What about clearing
+// them? Well, this is where Zig throws us a curve ball. Don't worry we'll
+// go through it step by step.
+//
+//
+//
+//
+//
+// ------------------------------------------------------------------------
+// Clearing bits with AND and NOT:
+// ------------------------------------------------------------------------
+// We can clear bits with the & (AND) bitwise operator, like so:
+
+// PORTB = 0b1110; // reset PORTB
+// PORTB = PORTB & 0b1011;
+// print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010
+//
+// - 0s clear bits when used in conjunction with a bitwise AND.
+// - 1s do nothing, thus preserving the original bits.
+//
+// -AND op- ---expanded---
+// __________ Clear only this bit.
+// /
+// 1110 1 1 1 0
+// & 1011 1 0 1 1 (bitmask)
+// ------ - - - -
+// = 1010 1 0 1 0 <- This bit was already cleared.
+// \_______\
+// \
+// These bits remain untouched because AND-ing with a
+// 1 preserves the original bit value whether 0 or 1.
+//
+// ------------------------------------------------------------------------
+// We can use the ~ (NOT) operator to easily create a bitmask like 1011:
+//
+// 1. First, shift the value 1 over two places with the bit-wise << (shift
+// left) operator as indicated below:
+// 1 << 0 -> 0001
+// 1 << 1 -> 0010
+// 1 << 2 -> 0100 <- The 1 has been shifted two places to the left
+// 1 << 3 -> 1000
+//
+// 2. The second step in creating our bitmask is to invert the bits
+// ~0100 -> 1011
+// in C we would write this as:
+// ~(1 << 2) -> 1011
+//
+// But if we try to compile ~(1 << 2) in Zig, we'll get an error:
+// unable to perform binary not operation on type 'comptime_int'
+//
+// Before Zig can invert our bits, it needs to know the number of
+// bits it's being asked to invert.
+//
+// We do this with the @as (cast as) built-in like this:
+// @as(u4, 1 << 2) -> 0100
+//
+// Finally, we can invert our new mask by placing the NOT ~ operator
+// before our expression, like this:
+// ~@as(u4, 1 << 2) -> 1011
+//
+// If you are offput by the fact that you can't simply invert bits like
+// you can in languages such as C without casting to a particular size
+// of integer, you're not alone. However, this is actually another
+// instance where Zig is really helpful because it protects you from
+// difficult to debug integer overflow bugs that can have you tearing
+// your hair out. In the interest of keeping things sane, Zig requires
+// you simply to tell it the size of number you are inverting. In the
+// words of Andrew Kelley, "If you want to invert the bits of an
+// integer, zig has to know how many bits there are."
+//
+// For more insight into the Zig team's position on why the language
+// takes the approach it does with the ~ operator, take a look at
+// Andrew's comments on the following github issue:
+// https://github.com/ziglang/zig/issues/1382#issuecomment-414459529
+//
+// Whew, so after all that what we end up with is:
+// PORTB = PORTB & ~@as(u4, 1 << 2);
+//
+// We can shorten this with the &= combined AND and assignment operator,
+// which applies the AND operator on PORTB and then reassigns PORTB. Here's
+// what that looks like:
+// PORTB &= ~@as(u4, 1 << 2);
+//
+
+// ------------------------------------------------------------------------
+// Conclusion
+// ------------------------------------------------------------------------
+//
+// While the examples in this quiz have used only 4-bit wide variables,
+// working with 8 bits is no different. Here's an example where we set
+// every other bit beginning with the two's place:
+
+// var PORTD: u8 = 0b0000_0000;
+// print("PORTD: {b:0>8}\n", .{PORTD});
+// PORTD |= (1 << 1);
+// PORTD = setBit(u8, PORTD, 3);
+// PORTD |= (1 << 5) | (1 << 7);
+// print("PORTD: {b:0>8} // set every other bit\n", .{PORTD});
+// PORTD = ~PORTD;
+// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
+// newline();
+//
+// // Here we clear every other bit beginning with the two's place.
+//
+// PORTD = 0b1111_1111;
+// print("PORTD: {b:0>8}\n", .{PORTD});
+// PORTD &= ~@as(u8, 1 << 1);
+// PORTD = clearBit(u8, PORTD, 3);
+// PORTD &= ~@as(u8, (1 << 5) | (1 << 7));
+// print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD});
+// PORTD = ~PORTD;
+// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
+// newline();
+
+// ----------------------------------------------------------------------------
+// Here are some helper functions for manipulating bits
+// ----------------------------------------------------------------------------
+
+// Functions for setting, clearing, and toggling a single bit
+fn setBit(comptime T: type, byte: T, comptime bit_pos: T) !T {
+ return byte | (1 << bit_pos);
+}
+
+test "setBit" {
+ try testing.expectEqual(setBit(u8, 0b0000_0000, 3), 0b0000_1000);
+}
+
+fn clearBit(comptime T: type, byte: T, comptime bit_pos: T) T {
+ return byte & ~@as(T, (1 << bit_pos));
+}
+
+test "clearBit" {
+ try testing.expectEqual(clearBit(u8, 0b1111_1111, 0), 0b1111_1110);
+}
+
+fn toggleBit(comptime T: type, byte: T, comptime bit_pos: T) T {
+ return byte ^ (1 << bit_pos);
+}
+
+test "toggleBit" {
+ var byte = toggleBit(u8, 0b0000_0000, 0);
+ try testing.expectEqual(byte, 0b0000_0001);
+ byte = toggleBit(u8, byte, 0);
+ try testing.expectEqual(byte, 0b0000_0000);
+}
+
+// ----------------------------------------------------------------------------
+// Some additional functions for setting, clearing, and toggling multiple bits
+// at once with a tuple because, hey, why not?
+// ----------------------------------------------------------------------------
+//
+
+fn createBitmask(comptime T: type, comptime bits: anytype) !T {
+ comptime var bitmask: T = 0;
+ inline for (bits) |bit| {
+ if (bit >= @bitSizeOf(T)) return error.BitPosTooLarge;
+ if (bit < 0) return error.BitPosTooSmall;
+
+ bitmask |= (1 << bit);
+ }
+ return bitmask;
+}
+
+test "creating bitmasks from a tuple" {
+ try testing.expectEqual(createBitmask(u8, .{0}), 0b0000_0001);
+ try testing.expectEqual(createBitmask(u8, .{1}), 0b0000_0010);
+ try testing.expectEqual(createBitmask(u8, .{2}), 0b0000_0100);
+ try testing.expectEqual(createBitmask(u8, .{3}), 0b0000_1000);
+ //
+ try testing.expectEqual(createBitmask(u8, .{ 0, 4 }), 0b0001_0001);
+ try testing.expectEqual(createBitmask(u8, .{ 1, 5 }), 0b0010_0010);
+ try testing.expectEqual(createBitmask(u8, .{ 2, 6 }), 0b0100_0100);
+ try testing.expectEqual(createBitmask(u8, .{ 3, 7 }), 0b1000_1000);
+
+ try testing.expectError(error.BitPosTooLarge, createBitmask(u4, .{4}));
+}
+
+fn setBits(byte: u8, bits: anytype) !u8 {
+ const bitmask = try createBitmask(u8, bits);
+ return byte | bitmask;
+}
+
+test "setBits" {
+ try testing.expectEqual(setBits(0b0000_0000, .{0}), 0b0000_0001);
+ try testing.expectEqual(setBits(0b0000_0000, .{7}), 0b1000_0000);
+
+ try testing.expectEqual(setBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
+ try testing.expectEqual(setBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
+
+ try testing.expectEqual(setBits(0b0000_0000, .{ 2, 3, 4, 5 }), 0b0011_1100);
+
+ try testing.expectError(error.BitPosTooLarge, setBits(0b1111_1111, .{8}));
+ try testing.expectError(error.BitPosTooSmall, setBits(0b1111_1111, .{-1}));
+}
+
+fn clearBits(comptime byte: u8, comptime bits: anytype) !u8 {
+ const bitmask: u8 = try createBitmask(u8, bits);
+ return byte & ~@as(u8, bitmask);
+}
+
+test "clearBits" {
+ try testing.expectEqual(clearBits(0b1111_1111, .{0}), 0b1111_1110);
+ try testing.expectEqual(clearBits(0b1111_1111, .{7}), 0b0111_1111);
+
+ try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
+ try testing.expectEqual(clearBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
+
+ try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 6, 7 }), 0b0011_1100);
+
+ try testing.expectError(error.BitPosTooLarge, clearBits(0b1111_1111, .{8}));
+ try testing.expectError(error.BitPosTooSmall, clearBits(0b1111_1111, .{-1}));
+}
+
+fn toggleBits(comptime byte: u8, comptime bits: anytype) !u8 {
+ const bitmask = try createBitmask(u8, bits);
+ return byte ^ bitmask;
+}
+
+test "toggleBits" {
+ try testing.expectEqual(toggleBits(0b0000_0000, .{0}), 0b0000_0001);
+ try testing.expectEqual(toggleBits(0b0000_0000, .{7}), 0b1000_0000);
+
+ try testing.expectEqual(toggleBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
+ try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
+
+ try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_0000);
+ try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3 }), 0b0000_0000);
+
+ try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 2, 4, 6 }), 0b0101_0101);
+
+ try testing.expectError(error.BitPosTooLarge, toggleBits(0b1111_1111, .{8}));
+ try testing.expectError(error.BitPosTooSmall, toggleBits(0b1111_1111, .{-1}));
+}
+
+// ----------------------------------------------------------------------------
+// Utility functions
+// ----------------------------------------------------------------------------
+
+fn newline() void {
+ print("\n", .{});
+}
+
+fn checkAnswer(expected: u4, answer: u4) void {
+ if (expected != answer) {
+ print("*************************************************************\n", .{});
+ print("= {b:0>4} <- INCORRECT! THE EXPECTED OUTPUT IS {b:0>4}\n", .{ answer, expected });
+ print("*************************************************************\n", .{});
+ } else {
+ print("= {b:0>4}", .{answer});
+ }
+ newline();
+}
diff --git a/patches/eowyn.sh b/patches/eowyn.sh
index 8cac450..afc2732 100755
--- a/patches/eowyn.sh
+++ b/patches/eowyn.sh
@@ -12,6 +12,12 @@
# using the patches in this directory and convey them
# to convalesce in the healed directory.
#
+delete_progress() {
+ progress_file=".progress.txt"
+ if [ -f $progress_file ]; then
+ rm $progress_file
+ fi
+}
set -e
# We check ourselves before we wreck ourselves.
@@ -23,9 +29,12 @@ fi
# Which version we have?
echo "Zig version" $(zig version)
-echo "Eowyn version 23.10.5.1, let's try our magic power."
+echo "Eowyn version 25.1.9, let's try our magic power."
echo ""
+# Remove progress file
+delete_progress
+
# Create directory of healing if it doesn't already exist.
mkdir -p patches/healed
@@ -54,3 +63,6 @@ zig fmt --check patches/healed
# Test the healed exercises. May the compiler have mercy upon us.
zig build -Dhealed
+
+# Remove progress file again
+delete_progress
diff --git a/patches/patches/046_optionals2.patch b/patches/patches/046_optionals2.patch
index 1b364a6..8fa01a2 100644
--- a/patches/patches/046_optionals2.patch
+++ b/patches/patches/046_optionals2.patch
@@ -1,5 +1,5 @@
---- exercises/046_optionals2.zig 2024-09-04 20:51:36.766783971 +0200
-+++ answers/046_optionals2.zig 2024-09-04 20:51:01.389400985 +0200
+--- exercises/046_optionals2.zig 2024-11-08 22:46:25.592875338 +0100
++++ answers/046_optionals2.zig 2024-11-08 22:46:20.699447951 +0100
@@ -22,7 +22,7 @@
const Elephant = struct {
diff --git a/patches/patches/058_quiz7.patch b/patches/patches/058_quiz7.patch
index 265b9e3..978fbb1 100644
--- a/patches/patches/058_quiz7.patch
+++ b/patches/patches/058_quiz7.patch
@@ -1,8 +1,8 @@
---- exercises/058_quiz7.zig 2023-10-03 22:15:22.125574535 +0200
-+++ answers/058_quiz7.zig 2023-10-05 20:04:07.106101152 +0200
+--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
++++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
@@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values
- // in a switch statement. Please capture both values as
+ // in a switch statement. Please capture each value as
// 'p' so the print statements work!
- .place => print("{s}", .{p.name}),
- .path => print("--{}->", .{p.dist}),
diff --git a/patches/patches/060_floats.patch b/patches/patches/060_floats.patch
index 404654a..9e64c6f 100644
--- a/patches/patches/060_floats.patch
+++ b/patches/patches/060_floats.patch
@@ -1,11 +1,11 @@
---- exercises/060_floats.zig 2023-11-06 19:45:03.609687304 +0100
-+++ answers/060_floats.zig 2023-11-06 19:44:49.249419994 +0100
+--- exercises/060_floats.zig 2025-03-03 20:23:40.255443963 +0400
++++ answers/060_floats.zig 2025-03-03 20:29:58.554854977 +0400
@@ -43,7 +43,7 @@
//
- // We'll convert this weight from pound to kilograms at a
- // conversion of 0.453592kg to the pound.
-- const shuttle_weight: f16 = 0.453592 * 4480e6;
-+ const shuttle_weight: f32 = 0.453592 * 4.480e6;
+ // We'll convert this weight from pounds to metric units at a
+ // conversion of 0.453592 kg to the pound.
+- const shuttle_weight: f16 = 0.453592 * 4480e3;
++ const shuttle_weight: f32 = 0.453592 * 4.480e3;
// By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see
diff --git a/patches/patches/065_builtins2.patch b/patches/patches/065_builtins2.patch
index d5da950..e5e6410 100644
--- a/patches/patches/065_builtins2.patch
+++ b/patches/patches/065_builtins2.patch
@@ -1,5 +1,5 @@
---- exercises/065_builtins2.zig 2024-09-02 19:15:56.569952315 +0200
-+++ answers/065_builtins2.zig 2024-09-02 19:13:44.280600350 +0200
+--- exercises/065_builtins2.zig 2024-11-02 16:58:30.607829441 +0100
++++ answers/065_builtins2.zig 2024-11-02 16:58:33.821220588 +0100
@@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here:
@@ -24,16 +24,16 @@
// (which is a zero-bit type that takes up no space at all!):
- if (fields[0].??? != void) {
+ if (fields[0].type != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[0].name});
+ print(" {s}", .{fields[0].name});
}
- if (fields[1].??? != void) {
+ if (fields[1].type != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[1].name});
+ print(" {s}", .{fields[1].name});
}
- if (fields[2].??? != void) {
+ if (fields[2].type != void) {
- print(" {s}", .{@typeInfo(Narcissus).@"struct".fields[2].name});
+ print(" {s}", .{fields[2].name});
}
diff --git a/patches/patches/082_anonymous_structs3.patch b/patches/patches/082_anonymous_structs3.patch
index b918011..0f71a94 100644
--- a/patches/patches/082_anonymous_structs3.patch
+++ b/patches/patches/082_anonymous_structs3.patch
@@ -1,5 +1,5 @@
---- exercises/082_anonymous_structs3.zig 2024-09-07 19:13:58.210327580 +0200
-+++ answers/082_anonymous_structs3.zig 2024-09-07 19:21:20.972733477 +0200
+--- exercises/082_anonymous_structs3.zig 2025-03-14 16:41:17.892873287 +0200
++++ answers/082_anonymous_structs3.zig 2025-03-14 16:40:56.043829543 +0200
@@ -82,14 +82,14 @@
// @typeInfo(Circle).@"struct".fields
//
@@ -17,7 +17,7 @@
// 3. Print the field's name, type, and value.
//
// Each 'field' in this loop is one of these:
-@@ -117,9 +117,9 @@
+@@ -119,9 +119,9 @@
//
// The first field should print as: "0"(bool):true
print("\"{s}\"({any}):{any} ", .{
diff --git a/patches/patches/097_bit_manipulation.patch b/patches/patches/097_bit_manipulation.patch
index 5303ee1..19ba876 100644
--- a/patches/patches/097_bit_manipulation.patch
+++ b/patches/patches/097_bit_manipulation.patch
@@ -1,5 +1,5 @@
---- exercises/097_bit_manipulation.zig 2023-10-03 22:15:22.125574535 +0200
-+++ answers/097_bit_manipulation.zig 2023-10-05 20:04:07.282771124 +0200
+--- exercises/097_bit_manipulation.zig 2025-05-12 21:25:03.395385743 +0200
++++ answers/097_bit_manipulation.zig 2025-05-12 21:22:57.472986976 +0200
@@ -80,7 +80,7 @@
y ^= x;
@@ -7,5 +7,5 @@
- ???;
+ x ^= y;
- print("x = {d}; y = {d}\n", .{ x, y });
+ print("x = {b}; y = {b}\n", .{ x, y });
}
diff --git a/patches/patches/099_formatting.patch b/patches/patches/099_formatting.patch
index 384bf86..a56b556 100644
--- a/patches/patches/099_formatting.patch
+++ b/patches/patches/099_formatting.patch
@@ -1,5 +1,5 @@
---- exercises/099_formatting.zig 2023-10-03 22:15:22.125574535 +0200
-+++ answers/099_formatting.zig 2023-10-05 20:04:07.292771311 +0200
+--- exercises/099_formatting.zig 2024-11-07 21:45:10.459123650 +0100
++++ answers/099_formatting.zig 2024-11-07 21:43:55.154345991 +0100
@@ -131,7 +131,7 @@
for (0..size) |b| {
// What formatting is needed here to make our columns
diff --git a/patches/patches/100_for4.patch b/patches/patches/100_for4.patch
index 3539be2..ad73e9a 100644
--- a/patches/patches/100_for4.patch
+++ b/patches/patches/100_for4.patch
@@ -7,5 +7,5 @@
- for (hex_nums, ???) |hn, ???| {
+ for (hex_nums, dec_nums) |hn, dn| {
if (hn != dn) {
- std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
+ print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return;
diff --git a/patches/patches/106_files.patch b/patches/patches/106_files.patch
index 5eb5a19..f40a33a 100644
--- a/patches/patches/106_files.patch
+++ b/patches/patches/106_files.patch
@@ -1,63 +1,16 @@
---- exercises/106_files.zig 2024-06-17 10:11:53.651439869 +0200
-+++ answers/106_files.zig 2024-06-17 10:21:50.697337653 +0200
-@@ -1,22 +1,22 @@
- //
- // Until now, we've only been printing our output in the console,
--// which is good enough for fighting aliens and hermit bookkeeping.
-+// which is good enough for fighting alien and hermit bookkeeping.
- //
--// However, many other tasks require some interaction with the file system,
-+// However, many other task require some interaction with the file system,
- // which is the underlying structure for organizing files on your computer.
- //
--// The file system provides a hierarchical structure for storing files
--// by organizing them into directories, which hold files and other directories,
--// thus creating a tree structure that can be navigated.
-+// The File System provide a hierarchical structure for storing files
-+// by organizing files into directories, which hold files and other directories,
-+// thus creating a tree structure for navigating.
- //
--// Fortunately, the Zig standard library provides a simple API for interacting
--// with the file system, see the detail documentation here:
-+// Fortunately, zig standard library provide a simple api for interacting
-+// with the file system, see the detail documentation here
- //
- // https://ziglang.org/documentation/master/std/#std.fs
- //
--// In this exercise, we'll try to:
--// - create a new directory,
--// - open a file in the directory,
-+// In this exercise, we'll try to
-+// - create a new directory
-+// - open a file in the directory
- // - write to the file.
- //
- // import std as always
-@@ -27,42 +27,42 @@
- const cwd: std.fs.Dir = std.fs.cwd();
-
- // then we'll try to make a new directory /output/
-- // to store our output files.
-+ // to put our output files.
- cwd.makeDir("output") catch |e| switch (e) {
-- // there is a chance you might want to run this
-+ // there are chance you might want to run this
- // program more than once and the path might already
-- // have been created, so we'll have to handle this error
-+ // been created, so we'll have to handle this error
+--- exercises/106_files.zig 2025-03-13 15:26:59.532367792 +0200
++++ answers/106_files.zig 2025-03-14 22:04:52.243435159 +0200
+@@ -35,7 +35,7 @@
// by doing nothing
//
// we want to catch error.PathAlreadyExists and do nothing
- ??? => {},
-- // if there's any other unexpected error we just propagate it through
+ error.PathAlreadyExists => {},
-+ // if is any other unexpected error we just propagate it through
+ // if there's any other unexpected error we just propagate it through
else => return e,
};
-
- // then we'll try to open our freshly created directory
-- // wait a minute...
-+ // wait a minute
+@@ -44,7 +44,7 @@
+ // wait a minute...
// opening a directory might fail!
// what should we do here?
- var output_dir: std.fs.Dir = cwd.openDir("output", .{});
@@ -65,36 +18,12 @@
defer output_dir.close();
// we try to open the file `zigling.txt`,
-- // and propagate any error up
-+ // and propagate the error up if there are any errors
- const file: std.fs.File = try output_dir.createFile("zigling.txt", .{});
- // it is a good habit to close a file after you are done with it
- // so that other programs can read it and prevent data corruption
+@@ -55,7 +55,7 @@
// but here we are not yet done writing to the file
-- // if only there were a keyword in Zig that
-- // allowed you to "defer" code execution to the end of the scope...
+ // if only there were a keyword in Zig that
+ // allowed you to "defer" code execution to the end of the scope...
- file.close();
-+ // if only there were a keyword in zig that
-+ // allows you "defer" code execute to the end of scope...
+ defer file.close();
-- // you are not allowed to move these two lines above the file closing line!
-+ // !you are not allowed to switch these two lines above the file closing line!
+ // you are not allowed to move these two lines above the file closing line!
const byte_written = try file.write("It's zigling time!");
- std.debug.print("Successfully wrote {d} bytes.\n", .{byte_written});
- }
- // to check if you actually write to the file, you can either,
--// 1. open the file in your text editor, or
-+// 1. open the file on your text editor, or
- // 2. print the content of the file in the console with the following command
- // >> cat ./output/zigling.txt
- //
-@@ -86,7 +86,7 @@
- //
- // Question:
- // - what should you do if you want to also read the file after opening it?
--// - go to the documentation of the struct `std.fs.Dir` here:
-+// - go to documentation of the struct `std.fs.Dir` here
- // https://ziglang.org/documentation/master/std/#std.fs.Dir
- // - can you find a function for opening a file? how about deleting a file?
- // - what kind of options can you use with those functions?
diff --git a/patches/patches/107_files2.patch b/patches/patches/107_files2.patch
index ebf8a7c..95aaa0c 100644
--- a/patches/patches/107_files2.patch
+++ b/patches/patches/107_files2.patch
@@ -1,55 +1,23 @@
---- exercises/107_files2.zig 2024-06-17 10:11:53.651439869 +0200
-+++ answers/107_files2.zig 2024-06-17 10:21:50.700671057 +0200
-@@ -4,17 +4,17 @@
- // - create a file {project_root}/output/zigling.txt
- // with content `It's zigling time!`(18 byte total)
- //
--// Now there's no point in writing to a file if we don't read from it, am I right?
--// Let's write a program to read the content of the file that we just created.
-+// Now there no point in writing to a file if we don't read from it am I right?
-+// let's write a program to read the content of the file that we just created.
- //
- // I am assuming that you've created the appropriate files for this to work.
- //
--// Alright, bud, lean in close. Here's the game plan.
-+// Alright, bud, lean in close here's the game plan.
- // - First, we open the {project_root}/output/ directory
- // - Secondly, we open file `zigling.txt` in that directory
--// - Then, we initalize an array of characters with all letter 'A', and print it
--// - After that, we read the content of the file into the array
--// - Finally, we print out the content we just read
-+// - then, we initalize an array of characters with all letter 'A', and print it
-+// - After that, we read the content of the file to the array
-+// - Finally, we print out the read content
-
- const std = @import("std");
-
-@@ -30,23 +30,23 @@
- const file = try output_dir.openFile("zigling.txt", .{});
- defer file.close();
-
-- // initalize an array of u8 with all letter 'A'
-- // we need to pick the size of the array, 64 seems like a good number
-+ // initalize an array of u8 with all letter 'A'.
-+ // we need to pick the size of the array, 64 seems like a good number.
- // fix the initalization below
+--- exercises/107_files2.zig 2025-03-13 15:26:59.532367792 +0200
++++ answers/107_files2.zig 2025-03-14 22:08:35.167953736 +0200
+@@ -33,7 +33,7 @@
+ // initialize an array of u8 with all letter 'A'
+ // we need to pick the size of the array, 64 seems like a good number
+ // fix the initialization below
- var content = ['A']*64;
+ var content = [_]u8{'A'} ** 64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
- // okay, seems like a threat of violence is not the answer in this case
-- // can you go here to find a way to read the content?
-+ // can you go here to find a way to read the content ?
+@@ -41,12 +41,12 @@
+ // can you go here to find a way to read the content?
// https://ziglang.org/documentation/master/std/#std.fs.File
- // hint: you might find two answers that are both vaild in this case
+ // hint: you might find two answers that are both valid in this case
- const bytes_read = zig_read_the_file_or_i_will_fight_you(&content);
+ const bytes_read = try file.read(&content);
-- // Woah, too screamy. I know you're excited for zigling time but tone it down a bit.
-- // Can you print only what we read from the file?
-+ // Woah, too screamy, I know you're excited for zigling time but tone it down a bit
-+ // Can you print only what we read from the file ?
+ // Woah, too screamy. I know you're excited for zigling time but tone it down a bit.
+ // Can you print only what we read from the file?
std.debug.print("Successfully Read {d} bytes: {s}\n", .{
bytes_read,
- content, // change this line only
diff --git a/patches/patches/109_vectors.patch b/patches/patches/109_vectors.patch
new file mode 100644
index 0000000..bf18cc0
--- /dev/null
+++ b/patches/patches/109_vectors.patch
@@ -0,0 +1,13 @@
+--- exercises/109_vectors.zig 2024-11-07 14:57:09.673383618 +0100
++++ answers/109_vectors.zig 2024-11-07 14:22:59.069150138 +0100
+@@ -121,8 +121,8 @@
+
+ const Vec4 = @Vector(4, f32);
+ fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
+- const abs_diff_vec = ???;
+- const max_diff = @reduce(???, abs_diff_vec);
++ const abs_diff_vec = @abs(a - b);
++ const max_diff = @reduce(.Max, abs_diff_vec);
+ return max_diff;
+ }
+
diff --git a/patches/patches/110_quiz9.patch b/patches/patches/110_quiz9.patch
new file mode 100644
index 0000000..9d9b864
--- /dev/null
+++ b/patches/patches/110_quiz9.patch
@@ -0,0 +1,56 @@
+--- exercises/110_quiz9.zig 2025-02-08 13:19:48.522641785 -0800
++++ answers/110_quiz9.zig 2025-02-10 17:42:04.525004335 -0800
+@@ -108,7 +108,7 @@
+ PORTB = 0b1100;
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("^ {b:0>4} // (bitmask)\n", .{0b0101});
+- PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
++ PORTB ^= (1 << 2) | (1 << 0);
+ checkAnswer(0b1001, PORTB);
+
+ newline();
+@@ -116,7 +116,7 @@
+ PORTB = 0b1100;
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("^ {b:0>4} // (bitmask)\n", .{0b0011});
+- PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
++ PORTB ^= (1 << 1) | (1 << 0);
+ checkAnswer(0b1111, PORTB);
+
+ newline();
+@@ -170,7 +170,7 @@
+ PORTB = 0b1001; // reset PORTB
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("| {b:0>4} // (bitmask)\n", .{0b0100});
+- PORTB = PORTB ??? (1 << 2); // What's missing here?
++ PORTB = PORTB | (1 << 2);
+ checkAnswer(0b1101, PORTB);
+
+ newline();
+@@ -178,7 +178,7 @@
+ PORTB = 0b1001; // reset PORTB
+ print(" {b:0>4} // (reset state)\n", .{PORTB});
+ print("| {b:0>4} // (bitmask)\n", .{0b0100});
+- PORTB ??? (1 << 2); // What's missing here?
++ PORTB |= (1 << 2);
+ checkAnswer(0b1101, PORTB);
+
+ newline();
+@@ -269,7 +269,7 @@
+ PORTB = 0b1110; // reset PORTB
+ print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
+ print("& {b:0>4} // (bitmask)\n", .{0b1011});
+- PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
++ PORTB = PORTB & ~@as(u4, 1 << 2);
+ checkAnswer(0b1010, PORTB);
+
+ newline();
+@@ -277,7 +277,7 @@
+ PORTB = 0b0111; // reset PORTB
+ print(" {b:0>4} // (reset state)\n", .{PORTB});
+ print("& {b:0>4} // (bitmask)\n", .{0b1110});
+- PORTB &= ~(1 << 0); // What's missing here?
++ PORTB &= ~@as(u4, 1 << 0);
+ checkAnswer(0b0110, PORTB);
+
+ newline();