add zig solutions

This commit is contained in:
Caleb Webber 2024-07-28 17:45:02 -04:00
commit aa2deeb687
42 changed files with 1719 additions and 0 deletions

View file

@ -0,0 +1,22 @@
{
"authors": [
"massivelivefun"
],
"contributors": [
"ee7"
],
"files": {
"solution": [
"collatz_conjecture.zig"
],
"test": [
"test_collatz_conjecture.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.",
"source": "An unsolved problem in mathematics named after mathematician Lothar Collatz",
"source_url": "https://en.wikipedia.org/wiki/3x_%2B_1_problem"
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"collatz-conjecture","id":"af061fffe6b244cf823d3ced4d948326","url":"https://exercism.org/tracks/zig/exercises/collatz-conjecture","handle":"seeplusplus","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit collatz_conjecture.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

View file

@ -0,0 +1,67 @@
# Collatz Conjecture
Welcome to Collatz Conjecture on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
The Collatz Conjecture or 3x+1 problem can be summarized as follows:
Take any positive integer n.
If n is even, divide n by 2 to get n / 2.
If n is odd, multiply n by 3 and add 1 to get 3n + 1.
Repeat the process indefinitely.
The conjecture states that no matter which number you start with, you will always reach 1 eventually.
Given a number n, return the number of steps required to reach 1.
## Examples
Starting with n = 12, the steps would be as follows:
0. 12
1. 6
2. 3
3. 10
4. 5
5. 16
6. 8
7. 4
8. 2
9. 1
Resulting in 9 steps.
So for input n = 12, the return value would be 9.
## Error handling
For this exercise you must add an error set `ComputationError` that contains the `IllegalArgument` error.
Remember to make it public!
The `steps` function must return `ComputationError.IllegalArgument` when its input is equal to zero.
Later exercises will usually omit explicit instructions like this.
In general, Exercism expects you to read the test file when implementing your solution.
For more details about errors in Zig, see:
- [Learning Zig - Errors][learning-zig-errors]
- [Ziglings - Exercise 21][ziglings-exercise-21]
- [Zighelp - Errors][zighelp-errors]
[learning-zig-errors]: https://www.openmymind.net/learning_zig/language_overview_2/#errors
[zighelp-errors]: https://zighelp.org/chapter-1/#errors
[ziglings-exercise-21]: https://codeberg.org/ziglings/exercises/src/commit/0d46acfa02d0c29fdfb3651e82a77284dd8f2e00/exercises/021_errors.zig
## Source
### Created by
- @massivelivefun
### Contributed to by
- @ee7
### Based on
An unsolved problem in mathematics named after mathematician Lothar Collatz - https://en.wikipedia.org/wiki/3x_%2B_1_problem

View file

@ -0,0 +1,20 @@
// Please implement the `ComputationError.IllegalArgument` error.
pub const ComputationError = error{IllegalArgument};
pub fn steps(number: usize) anyerror!usize {
if (number == 0) {
return ComputationError.IllegalArgument;
}
return collatz(number, 0);
}
fn collatz(number: usize, acc: usize) usize {
if (number == 1) {
return acc;
}
const next = if (number % 2 == 0) number / 2 else number * 3 + 1;
return collatz(next, acc + 1);
}

View file

@ -0,0 +1,8 @@
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const i = if (2 >= 2) 100 else 50;
print("your number was {d}", .{i});
}

View file

@ -0,0 +1,35 @@
const std = @import("std");
const testing = std.testing;
const collatz_conjecture = @import("collatz_conjecture.zig");
const ComputationError = collatz_conjecture.ComputationError;
test "zero steps for one" {
const expected: usize = 0;
const actual = try collatz_conjecture.steps(1);
try testing.expectEqual(expected, actual);
}
test "divide if even" {
const expected: usize = 4;
const actual = try collatz_conjecture.steps(16);
try testing.expectEqual(expected, actual);
}
test "even and odd steps" {
const expected: usize = 9;
const actual = try collatz_conjecture.steps(12);
try testing.expectEqual(expected, actual);
}
test "large number of even and odd steps" {
const expected: usize = 152;
const actual = try collatz_conjecture.steps(1_000_000);
try testing.expectEqual(expected, actual);
}
test "zero is an error" {
const expected = ComputationError.IllegalArgument;
const actual = collatz_conjecture.steps(0);
try testing.expectError(expected, actual);
}

View file

@ -0,0 +1,19 @@
{
"authors": [
"massivelivefun"
],
"files": {
"solution": [
"difference_of_squares.zig"
],
"test": [
"test_difference_of_squares.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.",
"source": "Problem 6 at Project Euler",
"source_url": "https://projecteuler.net/problem=6"
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"difference-of-squares","id":"a1fe4d3d6da444a8b36f9cc0e50182d0","url":"https://exercism.org/tracks/zig/exercises/difference-of-squares","handle":"seeplusplus","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit difference_of_squares.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

View file

@ -0,0 +1,29 @@
# Difference of Squares
Welcome to Difference of Squares on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.
The square of the sum of the first ten natural numbers is
(1 + 2 + ... + 10)² = 55² = 3025.
The sum of the squares of the first ten natural numbers is
1² + 2² + ... + 10² = 385.
Hence the difference between the square of the sum of the first ten natural numbers and the sum of the squares of the first ten natural numbers is 3025 - 385 = 2640.
You are not expected to discover an efficient solution to this yourself from first principles; research is allowed, indeed, encouraged.
Finding the best algorithm for the problem is a key skill in software engineering.
## Source
### Created by
- @massivelivefun
### Based on
Problem 6 at Project Euler - https://projecteuler.net/problem=6

View file

@ -0,0 +1,24 @@
const print = @import("std").debug.print;
const SomeStruct = struct { inner: ?usize = null };
pub fn Box(comptime T: type) type {
return struct {
inner: ?*T,
fn drop(self: *Box(T)) void {
self.inner.?.* = SomeStruct{ .inner = 1000 };
}
};
}
pub fn main() void {
var s = SomeStruct{};
var box = Box(SomeStruct){ .inner = &s };
s.inner = 100;
print("{any}\n", .{box.inner == &s});
print("{any}\n", .{box.inner.?.inner});
box.drop();
print("{any}\n", .{box.inner == &s});
print("{any}\n", .{box.inner});
print("{any}\n", .{s});
}

View file

@ -0,0 +1,16 @@
const pow = @import("std").math.pow;
pub fn squareOfSum(n: usize) usize {
return pow(usize, (n * (n + 1) / 2), 2);
}
pub fn sumOfSquares(number: usize) usize {
var tot: usize = 0;
for (0..(number + 1)) |n| {
tot += pow(usize, n, 2);
}
return tot;
}
pub fn differenceOfSquares(number: usize) usize {
return squareOfSum(number) - sumOfSquares(number);
}

View file

@ -0,0 +1,10 @@
const std = @import("std");
const print = std.debug.print;
const pow = std.math.pow;
pub fn main() void {
// print("{d}", .{pow(usize, 3, 2)});
for (0..=10) |n| {
print("{d}", .{n});
}
}

View file

@ -0,0 +1,58 @@
const std = @import("std");
const testing = std.testing;
const difference_of_squares = @import("difference_of_squares.zig");
test "square of sum up to 1" {
const expected: usize = 1;
const actual = difference_of_squares.squareOfSum(1);
try testing.expectEqual(expected, actual);
}
test "square of sum up to 5" {
const expected: usize = 225;
const actual = difference_of_squares.squareOfSum(5);
try testing.expectEqual(expected, actual);
}
test "square of sum up to 100" {
const expected: usize = 25_502_500;
const actual = difference_of_squares.squareOfSum(100);
try testing.expectEqual(expected, actual);
}
test "sum of squares up to 1" {
const expected: usize = 1;
const actual = difference_of_squares.sumOfSquares(1);
try testing.expectEqual(expected, actual);
}
test "sum of squares up to 5" {
const expected: usize = 55;
const actual = difference_of_squares.sumOfSquares(5);
try testing.expectEqual(expected, actual);
}
test "sum of squares up to 100" {
const expected: usize = 338_350;
const actual = difference_of_squares.sumOfSquares(100);
try testing.expectEqual(expected, actual);
}
test "difference of squares up to 1" {
const expected: usize = 0;
const actual = difference_of_squares.differenceOfSquares(1);
try testing.expectEqual(expected, actual);
}
test "difference of squares up to 5" {
const expected: usize = 170;
const actual = difference_of_squares.differenceOfSquares(5);
try testing.expectEqual(expected, actual);
}
test "difference of squares up to 100" {
const expected: usize = 25_164_150;
const actual = difference_of_squares.differenceOfSquares(100);
try testing.expectEqual(expected, actual);
}

View file

@ -0,0 +1,18 @@
{
"authors": [
"ee7"
],
"files": {
"solution": [
"linked_list.zig"
],
"test": [
"test_linked_list.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Implement a doubly linked list.",
"source": "Classic computer science topic"
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"linked-list","id":"5e4f2325faa44e6caf53842c3815c3e5","url":"https://exercism.org/tracks/zig/exercises/linked-list","handle":"seeplusplus","is_requester":true,"auto_approve":false}

53
zig/linked-list/HELP.md Normal file
View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit linked_list.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

53
zig/linked-list/README.md Normal file
View file

@ -0,0 +1,53 @@
# Linked List
Welcome to Linked List on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Introduction
You are working on a project to develop a train scheduling system for a busy railway network.
You've been asked to develop a prototype for the train routes in the scheduling system.
Each route consists of a sequence of train stations that a given train stops at.
## Instructions
Your team has decided to use a doubly linked list to represent each train route in the schedule.
Each station along the train's route will be represented by a node in the linked list.
You don't need to worry about arrival and departure times at the stations.
Each station will simply be represented by a number.
Routes can be extended, adding stations to the beginning or end of a route.
They can also be shortened by removing stations from the beginning or the end of a route.
Sometimes a station gets closed down, and in that case the station needs to be removed from the route, even if it is not at the beginning or end of the route.
The size of a route is measured not by how far the train travels, but by how many stations it stops at.
~~~~exercism/note
The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
As the name suggests, it is a list of nodes that are linked together.
It is a list of "nodes", where each node links to its neighbor or neighbors.
In a **singly linked list** each node links only to the node that follows it.
In a **doubly linked list** each node links to both the node that comes before, as well as the node that comes after.
If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.
[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
~~~~
## Error handling
For this exercise, you don't need to think about errors.
In particular, the tests will never call `pop` or `shift` on an empty list.
## Source
### Created by
- @ee7
### Based on
Classic computer science topic

9
zig/linked-list/l.zig Normal file
View file

@ -0,0 +1,9 @@
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const u: usize = 100;
var p: ?*const usize = &u;
const t = p;
p = null;
print("{any}", .{t.?.*});
}

View file

@ -0,0 +1,97 @@
pub fn LinkedList(comptime T: type) type {
return struct {
// Please implement the doubly linked `Node` (replacing each `void`).
pub const Node = struct {
prev: ?*Node = null,
next: ?*Node = null,
data: T,
};
// Please implement the fields of the linked list (replacing each `void`).
first: ?*Node = null,
last: ?*Node = null,
len: usize = 0,
// Please implement the below methods.
// You need to add the parameters to each method.
pub fn push(self: *LinkedList(T), item: *Node) void {
defer self.len += 1;
if (self.len == 0) {
self.first = item;
} else {
self.last.?.next = item;
item.prev = self.last;
}
self.last = item;
}
pub fn pop(self: *LinkedList(T)) ?*Node {
defer self.len -= 1;
const last = self.last.?;
if (last.prev != null) {
self.last = last.prev;
self.last.?.next = null;
last.prev = null;
} else {
self.last = null;
self.first = null;
}
return last;
}
pub fn shift(self: *LinkedList(T)) ?*Node {
defer self.len -= 1;
const first = self.first.?;
if (first != self.last) {
self.first = first.next.?;
self.first.?.prev = null;
first.next = null;
} else {
self.first = null;
self.last = null;
}
return first;
}
pub fn unshift(self: *LinkedList(T), item: *Node) void {
defer self.len += 1;
if (self.len != 0) {
const first = self.first;
first.?.prev = item;
item.next = first;
} else {
self.last = item;
}
self.first = item;
}
pub fn delete(self: *LinkedList(T), item: *Node) void {
var n: ?*Node = self.first;
while (n != item and n != null) {
n = n.?.next;
}
if (n != null) {
defer self.len -= 1;
const prev = n.?.prev;
const next = n.?.next;
if (prev != null) {
prev.?.next = next;
}
if (next != null) {
next.?.prev = prev;
}
if (self.last == n) {
self.last = n.?.prev;
}
if (self.first == n) {
self.first = n.?.next;
}
n.?.prev = null;
n.?.next = null;
}
}
};
}

View file

@ -0,0 +1,222 @@
const std = @import("std");
const testing = std.testing;
const linked_list = @import("linked_list.zig");
const List = linked_list.LinkedList(usize);
test "pop gets element from the list" {
var list = List{};
var a = List.Node{ .data = 7 };
list.push(&a);
try testing.expectEqual(@as(usize, 7), list.pop().?.data);
}
test "push/pop respectively add/remove at the end of the list" {
var list = List{};
var a = List.Node{ .data = 11 };
var b = List.Node{ .data = 13 };
list.push(&a);
list.push(&b);
try testing.expectEqual(@as(usize, 13), list.pop().?.data);
try testing.expectEqual(@as(usize, 11), list.pop().?.data);
}
test "shift gets an element from the list" {
var list = List{};
var a = List.Node{ .data = 17 };
list.push(&a);
try testing.expectEqual(@as(usize, 17), list.shift().?.data);
}
test "shift gets first element from the list" {
var list = List{};
var a = List.Node{ .data = 23 };
var b = List.Node{ .data = 5 };
list.push(&a);
list.push(&b);
try testing.expectEqual(@as(usize, 23), list.shift().?.data);
try testing.expectEqual(@as(usize, 5), list.shift().?.data);
}
test "unshift adds element at start of the list" {
var list = List{};
var a = List.Node{ .data = 23 };
var b = List.Node{ .data = 5 };
list.unshift(&a);
list.unshift(&b);
try testing.expectEqual(@as(usize, 5), list.shift().?.data);
try testing.expectEqual(@as(usize, 23), list.shift().?.data);
}
test "pop, push, shift, and unshift can be used in any order" {
var list = List{};
var a = List.Node{ .data = 1 };
var b = List.Node{ .data = 2 };
var c = List.Node{ .data = 3 };
var d = List.Node{ .data = 4 };
var e = List.Node{ .data = 5 };
list.push(&a);
list.push(&b);
try testing.expectEqual(@as(usize, 2), list.pop().?.data);
list.push(&c);
try testing.expectEqual(@as(usize, 1), list.shift().?.data);
list.unshift(&d);
list.push(&e);
try testing.expectEqual(@as(usize, 4), list.shift().?.data);
try testing.expectEqual(@as(usize, 5), list.pop().?.data);
try testing.expectEqual(@as(usize, 3), list.shift().?.data);
}
test "count an empty list" {
const list = List{};
try testing.expectEqual(@as(usize, 0), list.len);
}
test "count a list with items" {
var list = List{};
var a = List.Node{ .data = 37 };
var b = List.Node{ .data = 1 };
list.push(&a);
list.push(&b);
try testing.expectEqual(@as(usize, 2), list.len);
}
test "count is correct after mutation" {
var list = List{};
var a = List.Node{ .data = 31 };
var b = List.Node{ .data = 43 };
list.push(&a);
try testing.expectEqual(@as(usize, 1), list.len);
list.unshift(&b);
try testing.expectEqual(@as(usize, 2), list.len);
_ = list.shift();
try testing.expectEqual(@as(usize, 1), list.len);
_ = list.pop();
try testing.expectEqual(@as(usize, 0), list.len);
}
test "popping to empty doesn't break the list" {
var list = List{};
var a = List.Node{ .data = 41 };
var b = List.Node{ .data = 59 };
var c = List.Node{ .data = 47 };
list.push(&a);
list.push(&b);
_ = list.pop();
_ = list.pop();
list.push(&c);
try testing.expectEqual(@as(usize, 1), list.len);
try testing.expectEqual(@as(usize, 47), list.pop().?.data);
}
test "shifting to empty doesn't break the list" {
var list = List{};
var a = List.Node{ .data = 41 };
var b = List.Node{ .data = 59 };
var c = List.Node{ .data = 47 };
list.push(&a);
list.push(&b);
_ = list.shift();
_ = list.shift();
list.push(&c);
try testing.expectEqual(@as(usize, 1), list.len);
try testing.expectEqual(@as(usize, 47), list.shift().?.data);
}
test "deletes the only element" {
var list = List{};
var a = List.Node{ .data = 61 };
list.push(&a);
list.delete(&a);
try testing.expectEqual(@as(usize, 0), list.len);
}
test "deletes the element with the specified value from the list" {
var list = List{};
var a = List.Node{ .data = 71 };
var b = List.Node{ .data = 83 };
var c = List.Node{ .data = 79 };
list.push(&a);
list.push(&b);
list.push(&c);
list.delete(&b);
try testing.expectEqual(@as(usize, 2), list.len);
try testing.expectEqual(@as(usize, 79), list.pop().?.data);
try testing.expectEqual(@as(usize, 71), list.shift().?.data);
}
test "deletes the element with the specified value from the list, re-assigns tail" {
var list = List{};
var a = List.Node{ .data = 71 };
var b = List.Node{ .data = 83 };
var c = List.Node{ .data = 79 };
list.push(&a);
list.push(&b);
list.push(&c);
list.delete(&b);
try testing.expectEqual(@as(usize, 2), list.len);
try testing.expectEqual(@as(usize, 79), list.pop().?.data);
try testing.expectEqual(@as(usize, 71), list.pop().?.data);
}
test "deletes the element with the specified value from the list, re-assigns head" {
var list = List{};
var a = List.Node{ .data = 71 };
var b = List.Node{ .data = 83 };
var c = List.Node{ .data = 79 };
list.push(&a);
list.push(&b);
list.push(&c);
list.delete(&b);
try testing.expectEqual(@as(usize, 2), list.len);
try testing.expectEqual(@as(usize, 71), list.shift().?.data);
try testing.expectEqual(@as(usize, 79), list.shift().?.data);
}
test "deletes the first of two elements" {
var list = List{};
var a = List.Node{ .data = 97 };
var b = List.Node{ .data = 101 };
list.push(&a);
list.push(&b);
list.delete(&a);
try testing.expectEqual(@as(usize, 1), list.len);
try testing.expectEqual(@as(usize, 101), list.pop().?.data);
}
test "deletes the second of two elements" {
var list = List{};
var a = List.Node{ .data = 97 };
var b = List.Node{ .data = 101 };
list.push(&a);
list.push(&b);
list.delete(&b);
try testing.expectEqual(@as(usize, 1), list.len);
try testing.expectEqual(@as(usize, 97), list.pop().?.data);
}
test "delete does not modify the list if the element is not found" {
var list = List{};
var a = List.Node{ .data = 89 };
var b = List.Node{ .data = 103 };
list.push(&a);
list.delete(&b);
try testing.expectEqual(@as(usize, 1), list.len);
}
test "deletes only the first occurrence" {
var list = List{};
var a = List.Node{ .data = 73 };
var b = List.Node{ .data = 9 };
var c = List.Node{ .data = 9 };
var d = List.Node{ .data = 107 };
list.push(&a);
list.push(&b);
list.push(&c);
list.push(&d);
list.delete(&b);
try testing.expectEqual(@as(usize, 3), list.len);
try testing.expectEqual(@as(usize, 107), list.pop().?.data);
try testing.expectEqual(@as(usize, 9), list.pop().?.data);
try testing.expectEqual(@as(usize, 73), list.pop().?.data);
}

View file

@ -0,0 +1,19 @@
{
"authors": [
"massivelivefun"
],
"files": {
"solution": [
"resistor_color.zig"
],
"test": [
"test_resistor_color.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Convert a resistor band's color to its numeric representation.",
"source": "Maud de Vries, Erik Schierboom",
"source_url": "https://github.com/exercism/problem-specifications/issues/1458"
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"resistor-color","id":"6f9b21444bb54050a08fc1546ff89370","url":"https://exercism.org/tracks/zig/exercises/resistor-color","handle":"seeplusplus","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit resistor_color.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

View file

@ -0,0 +1,54 @@
# Resistor Color
Welcome to Resistor Color on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
For this exercise, you need to know two things about them:
- Each resistor has a resistance value.
- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values.
Each band has a position and a numeric value.
The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number.
In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands.
These colors are encoded as follows:
- Black: 0
- Brown: 1
- Red: 2
- Orange: 3
- Yellow: 4
- Green: 5
- Blue: 6
- Violet: 7
- Grey: 8
- White: 9
The goal of this exercise is to create a way:
- to look up the numerical value associated with a particular color band
- to list the different band colors
Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array:
Better Be Right Or Your Great Big Values Go Wrong.
More information on the color encoding of resistors can be found in the [Electronic color code Wikipedia article][e-color-code].
[e-color-code]: https://en.wikipedia.org/wiki/Electronic_color_code
## Source
### Created by
- @massivelivefun
### Based on
Maud de Vries, Erik Schierboom - https://github.com/exercism/problem-specifications/issues/1458

View file

@ -0,0 +1,9 @@
pub const ColorBand = enum { black, brown, red, orange, yellow, green, blue, violet, grey, white };
pub fn colorCode(color: ColorBand) usize {
return @intFromEnum(color);
}
pub fn colors() []const ColorBand {
return &[_]ColorBand{ .black, .brown, .red, .orange, .yellow, .green, .blue, .violet, .grey, .white };
}

View file

@ -0,0 +1,32 @@
const std = @import("std");
const testing = std.testing;
const resistor_color = @import("resistor_color.zig");
const ColorBand = resistor_color.ColorBand;
test "black" {
const expected: usize = 0;
const actual = resistor_color.colorCode(.black);
try testing.expectEqual(expected, actual);
}
test "white" {
const expected: usize = 9;
const actual = resistor_color.colorCode(.white);
try testing.expectEqual(expected, actual);
}
test "orange" {
const expected: usize = 3;
const actual = resistor_color.colorCode(.orange);
try testing.expectEqual(expected, actual);
}
test "colors" {
const expected = &[_]ColorBand{
.black, .brown, .red, .orange, .yellow,
.green, .blue, .violet, .grey, .white,
};
const actual = resistor_color.colors();
try testing.expectEqualSlices(ColorBand, expected, actual);
}

View file

@ -0,0 +1,19 @@
{
"authors": [
"ee7"
],
"files": {
"solution": [
"sum_of_multiples.zig"
],
"test": [
"test_sum_of_multiples.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.",
"source": "A variation on Problem 1 at Project Euler",
"source_url": "https://projecteuler.net/problem=1"
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"sum-of-multiples","id":"abcf1217d71e4cf4b2679b7c10738eb2","url":"https://exercism.org/tracks/zig/exercises/sum-of-multiples","handle":"seeplusplus","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit sum_of_multiples.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

View file

@ -0,0 +1,33 @@
# Sum of Multiples
Welcome to Sum of Multiples on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Given a list of factors and a limit, add up all the unique multiples of the factors that are less than the limit.
All inputs will be greater than or equal to zero.
## Example
Suppose the limit is 20 and the list of factors is [3, 5].
We need to find the sum of all unique multiples of 3 and 5 that are less than 20.
Multiples of 3 less than 20: 3, 6, 9, 12, 15, 18
Multiples of 5 less than 20: 5, 10, 15
The unique multiples are: 3, 5, 6, 9, 10, 12, 15, 18
The sum of the unique multiples is: 3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78
So, the answer is 78.
## Source
### Created by
- @ee7
### Based on
A variation on Problem 1 at Project Euler - https://projecteuler.net/problem=1

View file

@ -0,0 +1,21 @@
const std = @import("std");
const mem = std.mem;
const FactorErrors = error{FactorOfZero};
pub fn sum(allocator: mem.Allocator, factors: []const u32, limit: u32) !u64 {
_ = allocator;
var s: u64 = 0;
for (0..limit) |i| {
for (factors) |f| {
if (f == 0) {
continue;
}
if (i % f == 0) {
s += i;
break;
}
}
}
return s;
}

View file

@ -0,0 +1,141 @@
const std = @import("std");
const testing = std.testing;
const sum = @import("sum_of_multiples.zig").sum;
test "no multiples within limit" {
const expected: u64 = 0;
const factors = [_]u32{ 3, 5 };
const limit = 1;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "one factor has multiples within limit" {
const expected: u64 = 3;
const factors = [_]u32{ 3, 5 };
const limit = 4;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "more than one multiple within limit" {
const expected: u64 = 9;
const factors = [_]u32{3};
const limit = 7;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "more than one factor with multiples within limit" {
const expected: u64 = 23;
const factors = [_]u32{ 3, 5 };
const limit = 10;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "each multiple is only counted once" {
const expected: u64 = 2318;
const factors = [_]u32{ 3, 5 };
const limit = 100;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "a much larger limit" {
const expected: u64 = 233_168;
const factors = [_]u32{ 3, 5 };
const limit = 1000;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "three factors" {
const expected: u64 = 51;
const factors = [_]u32{ 7, 13, 17 };
const limit = 20;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "factors not relatively prime" {
const expected: u64 = 30;
const factors = [_]u32{ 4, 6 };
const limit = 15;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "some pairs of factors relatively prime and some not" {
const expected: u64 = 4419;
const factors = [_]u32{ 5, 6, 8 };
const limit = 150;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "one factor is a multiple of another" {
const expected: u64 = 275;
const factors = [_]u32{ 5, 25 };
const limit = 51;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "much larger factors" {
const expected: u64 = 2_203_160;
const factors = [_]u32{ 43, 47 };
const limit = 10_000;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "all numbers are multiples of 1" {
const expected: u64 = 4_950;
const factors = [_]u32{1};
const limit = 100;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "no factors means an empty sum" {
const expected: u64 = 0;
const factors = [_]u32{};
const limit = 10_000;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "the only multiple of 0 is 0" {
const expected: u64 = 0;
const factors = [_]u32{0};
const limit = 1;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "the factor 0 does not affect the sum of multiples of other factors" {
const expected: u64 = 3;
const factors = [_]u32{ 3, 0 };
const limit = 4;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "solutions using include-exclude must extend to cardinality greater than 3" {
const expected: u64 = 39_614_537;
const factors = [_]u32{ 2, 3, 5, 7, 11 };
const limit = 10_000;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}
test "sum is greater than maximum value of u32" {
// Note that for a u32 `limit`, the maximum sum of multiples fits in a u64.
const expected: u64 = 4_500_000_000;
const factors = [_]u32{100_000_000};
const limit = 1_000_000_000;
const actual = try sum(testing.allocator, &factors, limit);
try testing.expectEqual(expected, actual);
}

View file

@ -0,0 +1,18 @@
{
"authors": [
"ee7"
],
"files": {
"solution": [
"word_count.zig"
],
"test": [
"test_word_count.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Given a phrase, count the occurrences of each word in that phrase.",
"source": "This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour."
}

View file

@ -0,0 +1 @@
{"track":"zig","exercise":"word-count","id":"1a766232c629478b8e9943c94bbec1a1","url":"https://exercism.org/tracks/zig/exercises/word-count","handle":"seeplusplus","is_requester":true,"auto_approve":false}

53
zig/word-count/HELP.md Normal file
View file

@ -0,0 +1,53 @@
# Help
## Running the tests
Write your code in `<exercise_name>.zig`.
To run the tests for an exercise, run:
```bash
zig test test_exercise_name.zig
```
in the exercise's root directory (replacing `exercise_name` with the name of the exercise).
## Submitting your solution
You can submit your solution using the `exercism submit word_count.zig` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Zig track's documentation](https://exercism.org/docs/tracks/zig)
- The [Zig track's programming category on the forum](https://forum.exercism.org/c/programming/zig)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
- [The Zig Programming Language Documentation][documentation] is a great overview of all of the language features that Zig provides to those who use it.
- [Zig Learn][zig-learn] is an excellent primer that explains the language features that Zig has to offer.
- [Ziglings][ziglings] is highly recommended.
Learn Zig by fixing tiny broken programs.
- [The Zig Programming Language Discord][discord-zig] is the main [Discord][discord].
It provides a great way to get in touch with the Zig community at large, and get some quick, direct help for any Zig related problem.
- [#zig][irc] on irc.freenode.net is the main Zig IRC channel.
- [/r/Zig][reddit] is the main Zig subreddit.
- [Stack Overflow][stack-overflow] can be used to discover code snippets and solutions to problems that may have already asked and maybe solved by others.
[discord]: https://discordapp.com
[discord-zig]: https://discord.com/invite/gxsFFjE
[documentation]: https://ziglang.org/documentation/master
[irc]: https://webchat.freenode.net/?channels=%23zig
[reddit]: https://www.reddit.com/r/Zig
[stack-overflow]: https://stackoverflow.com/questions/tagged/zig
[zig-learn]: https://ziglearn.org/
[ziglings]: https://codeberg.org/ziglings/exercises

71
zig/word-count/README.md Normal file
View file

@ -0,0 +1,71 @@
# Word Count
Welcome to Word Count on Exercism's Zig Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Introduction
You teach English as a foreign language to high school students.
You've decided to base your entire curriculum on TV shows.
You need to analyze which words are used, and how often they're repeated.
This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes.
## Instructions
Your task is to count how many times each word occurs in a subtitle of a drama.
The subtitles from these dramas use only ASCII characters.
The characters often speak in casual English, using contractions like _they're_ or _it's_.
Though these contractions come from two words (e.g. _we are_), the contraction (_we're_) is considered a single word.
Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " ").
The only punctuation that does not separate words is the apostrophe in contractions.
Numbers are considered words.
If the subtitles say _It costs 100 dollars._ then _100_ will be its own word.
Words are case insensitive.
For example, the word _you_ occurs three times in the following sentence:
> You come back, you hear me? DO YOU HEAR ME?
The ordering of the word counts in the results doesn't matter.
Here's an example that incorporates several of the elements discussed above:
- simple words
- contractions
- numbers
- case insensitive words
- punctuation (including apostrophes) to separate words
- different forms of whitespace to separate words
`"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.`
The mapping for this subtitle would be:
```text
123: 1
agent: 1
cried: 1
fled: 1
i: 1
password: 2
so: 1
special: 1
that's: 1
the: 2
```
## Source
### Created by
- @ee7
### Based on
This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour.

19
zig/word-count/slices.zig Normal file
View file

@ -0,0 +1,19 @@
const print = @import("std").debug.print;
pub fn main() void {
const numbers = [_]u8{ 1, 2, 3, 4, 5 };
m(&numbers, numbers[3..4]);
}
fn m(n: []const u8, s: []const u8) void {
print("{any}\n", .{s});
const max_ptr = @intFromPtr(n.ptr) + n.len;
if (max_ptr > (@intFromPtr(s.ptr) + s.len)) {
const start = @intFromPtr(s.ptr) - @intFromPtr(n.ptr);
const end = s.len + start + 1;
return m(n, n[start..end]);
} else {
return;
}
}

View file

@ -0,0 +1,14 @@
const std = @import("std");
const print = std.debug.print;
const countWords = @import("./word_count.zig").countWords;
pub fn main() !void {
const alloc = std.heap.page_allocator;
const m = try countWords(alloc, "go Go GO Stop stop");
print("count: {d}\n", .{m.count()});
var iter = m.keyIterator();
while (iter.next()) |k| {
print("{s}: {d}\n", .{ k.*, m.get(k.*).? });
}
// print("{any}", .{m});
}

View file

@ -0,0 +1,164 @@
const std = @import("std");
const testing = std.testing;
const countWords = @import("word_count.zig").countWords;
fn freeKeysAndDeinit(self: *std.StringHashMap(u32)) void {
var iter = self.keyIterator();
while (iter.next()) |key_ptr| {
self.allocator.free(key_ptr.*);
}
self.deinit();
}
test "count one word" {
const s = "word";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 1), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("word"));
}
test "count one of each word" {
const s = "one of each";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 3), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("one"));
try testing.expectEqual(@as(?u32, 1), map.get("of"));
try testing.expectEqual(@as(?u32, 1), map.get("each"));
}
test "multiple occurrences of a word" {
const s = "one fish two fish red fish blue fish";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 5), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("one"));
try testing.expectEqual(@as(?u32, 4), map.get("fish"));
try testing.expectEqual(@as(?u32, 1), map.get("two"));
try testing.expectEqual(@as(?u32, 1), map.get("red"));
try testing.expectEqual(@as(?u32, 1), map.get("blue"));
}
test "handles cramped lists" {
const s = "one,two,three";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 3), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("one"));
try testing.expectEqual(@as(?u32, 1), map.get("two"));
try testing.expectEqual(@as(?u32, 1), map.get("three"));
}
test "handles expanded lists" {
const s = "one,\ntwo,\nthree";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 3), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("one"));
try testing.expectEqual(@as(?u32, 1), map.get("two"));
try testing.expectEqual(@as(?u32, 1), map.get("three"));
}
test "ignore punctuation" {
const s = "car: carpet as java: javascript!!&@$%^&";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 5), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("car"));
try testing.expectEqual(@as(?u32, 1), map.get("carpet"));
try testing.expectEqual(@as(?u32, 1), map.get("as"));
try testing.expectEqual(@as(?u32, 1), map.get("java"));
try testing.expectEqual(@as(?u32, 1), map.get("javascript"));
}
test "include numbers" {
const s = "testing, 1, 2 testing";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 3), map.count());
try testing.expectEqual(@as(?u32, 2), map.get("testing"));
try testing.expectEqual(@as(?u32, 1), map.get("1"));
try testing.expectEqual(@as(?u32, 1), map.get("2"));
}
test "normalize case" {
const s = "go Go GO Stop stop";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 2), map.count());
try testing.expectEqual(@as(?u32, 3), map.get("go"));
try testing.expectEqual(@as(?u32, 2), map.get("stop"));
}
test "with apostrophes" {
const s = "'First: don't laugh. Then: don't cry. You're getting it.'";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 8), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("first"));
try testing.expectEqual(@as(?u32, 2), map.get("don't"));
try testing.expectEqual(@as(?u32, 1), map.get("laugh"));
try testing.expectEqual(@as(?u32, 1), map.get("then"));
try testing.expectEqual(@as(?u32, 1), map.get("cry"));
try testing.expectEqual(@as(?u32, 1), map.get("you're"));
try testing.expectEqual(@as(?u32, 1), map.get("getting"));
try testing.expectEqual(@as(?u32, 1), map.get("it"));
}
test "with quotations" {
const s = "Joe can't tell between 'large' and large.";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 6), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("joe"));
try testing.expectEqual(@as(?u32, 1), map.get("can't"));
try testing.expectEqual(@as(?u32, 1), map.get("tell"));
try testing.expectEqual(@as(?u32, 1), map.get("between"));
try testing.expectEqual(@as(?u32, 2), map.get("large"));
try testing.expectEqual(@as(?u32, 1), map.get("and"));
}
test "substrings from the beginning" {
const s = "Joe can't tell between app, apple and a.";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 8), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("joe"));
try testing.expectEqual(@as(?u32, 1), map.get("can't"));
try testing.expectEqual(@as(?u32, 1), map.get("tell"));
try testing.expectEqual(@as(?u32, 1), map.get("between"));
try testing.expectEqual(@as(?u32, 1), map.get("app"));
try testing.expectEqual(@as(?u32, 1), map.get("apple"));
try testing.expectEqual(@as(?u32, 1), map.get("and"));
try testing.expectEqual(@as(?u32, 1), map.get("a"));
}
test "multiple spaces not detected as a word" {
const s = " multiple whitespaces";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 2), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("multiple"));
try testing.expectEqual(@as(?u32, 1), map.get("whitespaces"));
}
test "alternating word separators not detected as a word" {
const s = ",\n,one,\n ,two \n 'three'";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 3), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("one"));
try testing.expectEqual(@as(?u32, 1), map.get("two"));
try testing.expectEqual(@as(?u32, 1), map.get("three"));
}
test "quotation for word with apostrophe" {
const s = "can, can't, 'can't'";
var map = try countWords(testing.allocator, s);
defer freeKeysAndDeinit(&map);
try testing.expectEqual(@as(u32, 2), map.count());
try testing.expectEqual(@as(?u32, 1), map.get("can"));
try testing.expectEqual(@as(?u32, 2), map.get("can't"));
}

View file

@ -0,0 +1,74 @@
const std = @import("std");
const mem = std.mem;
const HashMap = std.HashMap;
const ArrayList = std.ArrayList;
const StringHashMap = std.StringHashMap(u32);
/// Returns the counts of the words in `s`.
/// Caller owns the returned memory.
/// "That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.
pub fn countWords(allocator: mem.Allocator, s: []const u8) !StringHashMap {
var hashMap = StringHashMap.init(allocator);
try read_words(allocator, s, 0, null, &hashMap);
return hashMap;
}
fn read_words(allocator: mem.Allocator, s: []const u8, index: usize, currentWord: ?[]const u8, words: *StringHashMap) !void {
const next_idx = index + 1;
const isIntraWord = index < s.len and (isWordLetter(s[index]) or
(s[index] == '\'' and isBetweenLetters(s, index)));
if (!isIntraWord and currentWord != null) {
const l = currentWord.?.len;
var word = try ArrayList(u8).initCapacity(allocator, l);
for (currentWord.?, 0..) |n, i| {
try word.insert(i, toLowerCase(n));
}
const kword = try word.toOwnedSlice();
const count = words.get(kword);
try words.put(kword, (count orelse 0) + 1);
if (count != null) {
defer allocator.free(kword);
}
}
if (index == s.len) {
return;
}
if (isIntraWord and currentWord == null) {
return read_words(allocator, s, next_idx, s[index..next_idx], words);
} else if (isIntraWord) {
return read_words(allocator, s, next_idx, growSubslice(s, currentWord.?), words);
} else {
return read_words(allocator, s, next_idx, null, words);
}
}
fn isBetweenLetters(s: []const u8, index: usize) bool {
return index > 0 and index < (s.len - 1) and isWordLetter(s[index - 1]) and isWordLetter(s[index + 1]);
}
fn isWordLetter(letter: u8) bool {
return (letter >= 'A' and letter <= 'Z') or (letter >= 'a' and letter <= 'z') or (letter >= '0' and letter <= '9');
}
fn isUpperCase(letter: u8) bool {
return letter >= 'A' and letter <= 'Z';
}
fn toLowerCase(letter: u8) u8 {
return if (isUpperCase(letter)) letter + 32 else letter;
}
fn growSubslice(root: []const u8, child: []const u8) []const u8 {
const max_ptr = @intFromPtr(root.ptr) + root.len;
if (max_ptr > (@intFromPtr(child.ptr) + child.len)) {
const start = @intFromPtr(child.ptr) - @intFromPtr(root.ptr);
const end = child.len + start + 1;
return root[start..end];
} else {
return child;
}
}