diff --git a/config.json b/config.json
index f5876925..412b06c4 100644
--- a/config.json
+++ b/config.json
@@ -815,6 +815,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 1
+ },
+ {
+ "slug": "line-up",
+ "name": "Line Up",
+ "uuid": "0b640c0b-b3a8-4830-babc-28ca370cac9d",
+ "practices": [],
+ "prerequisites": [],
+ "difficulty": 1
}
]
},
diff --git a/exercises/practice/line-up/.docs/instructions.md b/exercises/practice/line-up/.docs/instructions.md
new file mode 100644
index 00000000..9e686ecb
--- /dev/null
+++ b/exercises/practice/line-up/.docs/instructions.md
@@ -0,0 +1,19 @@
+# Instructions
+
+Given a name and a number, your task is to produce a sentence using that name and that number as an [ordinal numeral][ordinal-numeral].
+Yaʻqūb expects to use numbers from 1 up to 999.
+
+Rules:
+
+- Numbers ending in 1 (unless ending in 11) → `"st"`
+- Numbers ending in 2 (unless ending in 12) → `"nd"`
+- Numbers ending in 3 (unless ending in 13) → `"rd"`
+- All other numbers → `"th"`
+
+Examples:
+
+- `"Mary", 1` → `"Mary, you are the 1st customer we serve today. Thank you!"`
+- `"John", 12` → `"John, you are the 12th customer we serve today. Thank you!"`
+- `"Dahir", 162` → `"Dahir, you are the 162nd customer we serve today. Thank you!"`
+
+[ordinal-numeral]: https://en.wikipedia.org/wiki/Ordinal_numeral
diff --git a/exercises/practice/line-up/.docs/introduction.md b/exercises/practice/line-up/.docs/introduction.md
new file mode 100644
index 00000000..ea07268a
--- /dev/null
+++ b/exercises/practice/line-up/.docs/introduction.md
@@ -0,0 +1,7 @@
+# Introduction
+
+Your friend Yaʻqūb works the counter at a deli in town, slicing, weighing, and wrapping orders for a line of hungry customers that gets longer every day.
+Waiting customers are starting to lose track of who is next, so he wants numbered tickets they can use to track the order in which they arrive.
+
+To make the customers feel special, he does not want the ticket to have only a number on it.
+They shall get a proper English sentence with their name and number on it.
diff --git a/exercises/practice/line-up/.meta/config.json b/exercises/practice/line-up/.meta/config.json
new file mode 100644
index 00000000..e6ad6142
--- /dev/null
+++ b/exercises/practice/line-up/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "authors": [
+ "habere-et-dispertire"
+ ],
+ "files": {
+ "solution": [
+ "lib/LineUp.rakumod"
+ ],
+ "test": [
+ "t/line-up.rakutest"
+ ],
+ "example": [
+ ".meta/solutions/lib/LineUp.rakumod"
+ ]
+ },
+ "blurb": "Help lining up customers at Yaʻqūb's Deli.",
+ "source": "mk-mxp, based on previous work from Exercism contributors codedge and neenjaw",
+ "source_url": "https://forum.exercism.org/t/new-exercise-ordinal-numbers/19147"
+}
diff --git a/exercises/practice/line-up/.meta/solutions/lib/LineUp.rakumod b/exercises/practice/line-up/.meta/solutions/lib/LineUp.rakumod
new file mode 100644
index 00000000..4e7b0d25
--- /dev/null
+++ b/exercises/practice/line-up/.meta/solutions/lib/LineUp.rakumod
@@ -0,0 +1,11 @@
+unit module LineUp;
+
+sub deli-order (:$customer, :$ticket) is export {
+ sub ordinal ($num) {
+ return .value with first { $ticket.ends-with: .key }, ( <11 12 13 1 2 3> Z=>
);
+ return 'th'
+ }
+ sprintf
+ '%s, you are the %d%s customer we serve today. Thank you!',
+ $customer, $ticket, ordinal $ticket
+}
diff --git a/exercises/practice/line-up/.meta/solutions/t/line-up.rakutest b/exercises/practice/line-up/.meta/solutions/t/line-up.rakutest
new file mode 120000
index 00000000..31ef29e9
--- /dev/null
+++ b/exercises/practice/line-up/.meta/solutions/t/line-up.rakutest
@@ -0,0 +1 @@
+../../../t/line-up.rakutest
\ No newline at end of file
diff --git a/exercises/practice/line-up/.meta/template-data.yaml b/exercises/practice/line-up/.meta/template-data.yaml
new file mode 100644
index 00000000..5dd30b4f
--- /dev/null
+++ b/exercises/practice/line-up/.meta/template-data.yaml
@@ -0,0 +1,28 @@
+properties:
+ format:
+ test: |-
+ sprintf(q:to/END/, %case.Str.raku, %case.Int.raku, %case.raku, %case.raku);
+ cmp-ok(
+ deli-order(customer=>%s, ticket=>%s),
+ "eq",
+ %s,
+ %s,
+ );
+ END
+
+
+unit: module
+example: |-
+ sub deli-order (:$customer, :$ticket) is export {
+ sub ordinal ($num) {
+ return .value with first { $ticket.ends-with: .key }, ( <11 12 13 1 2 3> Z=>
);
+ return 'th'
+ }
+ sprintf
+ '%s, you are the %d%s customer we serve today. Thank you!',
+ $customer, $ticket, ordinal $ticket
+ }
+
+stub: |-
+ sub deli-order (:$customer, :$ticket) is export {
+ }
diff --git a/exercises/practice/line-up/.meta/tests.toml b/exercises/practice/line-up/.meta/tests.toml
new file mode 100644
index 00000000..36fdf1d0
--- /dev/null
+++ b/exercises/practice/line-up/.meta/tests.toml
@@ -0,0 +1,67 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[7760d1b8-4864-4db4-953b-0fa7c047dbc0]
+description = "format smallest non-exceptional ordinal numeral 4"
+
+[e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a]
+description = "format greatest single digit non-exceptional ordinal numeral 9"
+
+[f370aae9-7ae7-4247-90ce-e8ff8c6934df]
+description = "format non-exceptional ordinal numeral 5"
+
+[37f10dea-42a2-49de-bb92-0b690b677908]
+description = "format non-exceptional ordinal numeral 6"
+
+[d8dfb9a2-3a1f-4fee-9dae-01af3600054e]
+description = "format non-exceptional ordinal numeral 7"
+
+[505ec372-1803-42b1-9377-6934890fd055]
+description = "format non-exceptional ordinal numeral 8"
+
+[8267072d-be1f-4f70-b34a-76b7557a47b9]
+description = "format exceptional ordinal numeral 1"
+
+[4d8753cb-0364-4b29-84b8-4374a4fa2e3f]
+description = "format exceptional ordinal numeral 2"
+
+[8d44c223-3a7e-4f48-a0ca-78e67bf98aa7]
+description = "format exceptional ordinal numeral 3"
+
+[6c4f6c88-b306-4f40-bc78-97cdd583c21a]
+description = "format smallest two digit non-exceptional ordinal numeral 10"
+
+[e257a43f-d2b1-457a-97df-25f0923fc62a]
+description = "format non-exceptional ordinal numeral 11"
+
+[bb1db695-4d64-457f-81b8-4f5a2107e3f4]
+description = "format non-exceptional ordinal numeral 12"
+
+[60a3187c-9403-4835-97de-4f10ebfd63e2]
+description = "format non-exceptional ordinal numeral 13"
+
+[2bdcebc5-c029-4874-b6cc-e9bec80d603a]
+description = "format exceptional ordinal numeral 21"
+
+[74ee2317-0295-49d2-baf0-d56bcefa14e3]
+description = "format exceptional ordinal numeral 62"
+
+[b37c332d-7f68-40e3-8503-e43cbd67a0c4]
+description = "format exceptional ordinal numeral 100"
+
+[0375f250-ce92-4195-9555-00e28ccc4d99]
+description = "format exceptional ordinal numeral 101"
+
+[0d8a4974-9a8a-45a4-aca7-a9fb473c9836]
+description = "format non-exceptional ordinal numeral 112"
+
+[06b62efe-199e-4ce7-970d-4bf73945713f]
+description = "format exceptional ordinal numeral 123"
diff --git a/exercises/practice/line-up/lib/LineUp.rakumod b/exercises/practice/line-up/lib/LineUp.rakumod
new file mode 100644
index 00000000..fa00e51a
--- /dev/null
+++ b/exercises/practice/line-up/lib/LineUp.rakumod
@@ -0,0 +1,4 @@
+unit module LineUp;
+
+sub deli-order (:$customer, :$ticket) is export {
+}
diff --git a/exercises/practice/line-up/t/line-up.rakutest b/exercises/practice/line-up/t/line-up.rakutest
new file mode 100755
index 00000000..5ac816b4
--- /dev/null
+++ b/exercises/practice/line-up/t/line-up.rakutest
@@ -0,0 +1,139 @@
+#!/usr/bin/env raku
+use Test;
+use lib $?FILE.IO.parent(2).add('lib');
+use LineUp;
+
+cmp-ok( # begin: 7760d1b8-4864-4db4-953b-0fa7c047dbc0
+ deli-order(customer=>"Gianna", ticket=>4),
+ "eq",
+ "Gianna, you are the 4th customer we serve today. Thank you!",
+ "format smallest non-exceptional ordinal numeral 4",
+); # end: 7760d1b8-4864-4db4-953b-0fa7c047dbc0
+
+cmp-ok( # begin: e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a
+ deli-order(customer=>"Maarten", ticket=>9),
+ "eq",
+ "Maarten, you are the 9th customer we serve today. Thank you!",
+ "format greatest single digit non-exceptional ordinal numeral 9",
+); # end: e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a
+
+cmp-ok( # begin: f370aae9-7ae7-4247-90ce-e8ff8c6934df
+ deli-order(customer=>"Petronila", ticket=>5),
+ "eq",
+ "Petronila, you are the 5th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 5",
+); # end: f370aae9-7ae7-4247-90ce-e8ff8c6934df
+
+cmp-ok( # begin: 37f10dea-42a2-49de-bb92-0b690b677908
+ deli-order(customer=>"Attakullakulla", ticket=>6),
+ "eq",
+ "Attakullakulla, you are the 6th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 6",
+); # end: 37f10dea-42a2-49de-bb92-0b690b677908
+
+cmp-ok( # begin: d8dfb9a2-3a1f-4fee-9dae-01af3600054e
+ deli-order(customer=>"Kate", ticket=>7),
+ "eq",
+ "Kate, you are the 7th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 7",
+); # end: d8dfb9a2-3a1f-4fee-9dae-01af3600054e
+
+cmp-ok( # begin: 505ec372-1803-42b1-9377-6934890fd055
+ deli-order(customer=>"Maximiliano", ticket=>8),
+ "eq",
+ "Maximiliano, you are the 8th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 8",
+); # end: 505ec372-1803-42b1-9377-6934890fd055
+
+cmp-ok( # begin: 8267072d-be1f-4f70-b34a-76b7557a47b9
+ deli-order(customer=>"Mary", ticket=>1),
+ "eq",
+ "Mary, you are the 1st customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 1",
+); # end: 8267072d-be1f-4f70-b34a-76b7557a47b9
+
+cmp-ok( # begin: 4d8753cb-0364-4b29-84b8-4374a4fa2e3f
+ deli-order(customer=>"Haruto", ticket=>2),
+ "eq",
+ "Haruto, you are the 2nd customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 2",
+); # end: 4d8753cb-0364-4b29-84b8-4374a4fa2e3f
+
+cmp-ok( # begin: 8d44c223-3a7e-4f48-a0ca-78e67bf98aa7
+ deli-order(customer=>"Henriette", ticket=>3),
+ "eq",
+ "Henriette, you are the 3rd customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 3",
+); # end: 8d44c223-3a7e-4f48-a0ca-78e67bf98aa7
+
+cmp-ok( # begin: 6c4f6c88-b306-4f40-bc78-97cdd583c21a
+ deli-order(customer=>"Alvarez", ticket=>10),
+ "eq",
+ "Alvarez, you are the 10th customer we serve today. Thank you!",
+ "format smallest two digit non-exceptional ordinal numeral 10",
+); # end: 6c4f6c88-b306-4f40-bc78-97cdd583c21a
+
+cmp-ok( # begin: e257a43f-d2b1-457a-97df-25f0923fc62a
+ deli-order(customer=>"Jacqueline", ticket=>11),
+ "eq",
+ "Jacqueline, you are the 11th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 11",
+); # end: e257a43f-d2b1-457a-97df-25f0923fc62a
+
+cmp-ok( # begin: bb1db695-4d64-457f-81b8-4f5a2107e3f4
+ deli-order(customer=>"Juan", ticket=>12),
+ "eq",
+ "Juan, you are the 12th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 12",
+); # end: bb1db695-4d64-457f-81b8-4f5a2107e3f4
+
+cmp-ok( # begin: 60a3187c-9403-4835-97de-4f10ebfd63e2
+ deli-order(customer=>"Patricia", ticket=>13),
+ "eq",
+ "Patricia, you are the 13th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 13",
+); # end: 60a3187c-9403-4835-97de-4f10ebfd63e2
+
+cmp-ok( # begin: 2bdcebc5-c029-4874-b6cc-e9bec80d603a
+ deli-order(customer=>"Washi", ticket=>21),
+ "eq",
+ "Washi, you are the 21st customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 21",
+); # end: 2bdcebc5-c029-4874-b6cc-e9bec80d603a
+
+cmp-ok( # begin: 74ee2317-0295-49d2-baf0-d56bcefa14e3
+ deli-order(customer=>"Nayra", ticket=>62),
+ "eq",
+ "Nayra, you are the 62nd customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 62",
+); # end: 74ee2317-0295-49d2-baf0-d56bcefa14e3
+
+cmp-ok( # begin: b37c332d-7f68-40e3-8503-e43cbd67a0c4
+ deli-order(customer=>"John", ticket=>100),
+ "eq",
+ "John, you are the 100th customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 100",
+); # end: b37c332d-7f68-40e3-8503-e43cbd67a0c4
+
+cmp-ok( # begin: 0375f250-ce92-4195-9555-00e28ccc4d99
+ deli-order(customer=>"Zeinab", ticket=>101),
+ "eq",
+ "Zeinab, you are the 101st customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 101",
+); # end: 0375f250-ce92-4195-9555-00e28ccc4d99
+
+cmp-ok( # begin: 0d8a4974-9a8a-45a4-aca7-a9fb473c9836
+ deli-order(customer=>"Knud", ticket=>112),
+ "eq",
+ "Knud, you are the 112th customer we serve today. Thank you!",
+ "format non-exceptional ordinal numeral 112",
+); # end: 0d8a4974-9a8a-45a4-aca7-a9fb473c9836
+
+cmp-ok( # begin: 06b62efe-199e-4ce7-970d-4bf73945713f
+ deli-order(customer=>"Yma", ticket=>123),
+ "eq",
+ "Yma, you are the 123rd customer we serve today. Thank you!",
+ "format exceptional ordinal numeral 123",
+); # end: 06b62efe-199e-4ce7-970d-4bf73945713f
+
+done-testing;