diff options
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. + @@ -3,6 +3,7 @@ /answers/ /patches/healed/ /output/ +.progress.txt # Leave this in here for older zig versions /zig-cache/ @@ -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) @@ -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(); |
