diff --git a/config.json b/config.json
index c95b77ec4..07a5fe8d9 100644
--- a/config.json
+++ b/config.json
@@ -238,6 +238,19 @@
],
"difficulty": 1
},
+ {
+ "slug": "line-up",
+ "name": "Line Up",
+ "uuid": "734b83c6-e72d-47f2-830c-8ade1dc93662",
+ "practices": [
+ "numbers"
+ ],
+ "prerequisites": [
+ "if-then-else-expressions",
+ "strings"
+ ],
+ "difficulty": 1
+ },
{
"slug": "leap",
"name": "Leap",
diff --git a/exercises/Exercises.sln b/exercises/Exercises.sln
index d40a6472f..f3262c8d9 100644
--- a/exercises/Exercises.sln
+++ b/exercises/Exercises.sln
@@ -307,6 +307,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Satellite", "practice\satel
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FlowerField", "practice\flower-field\FlowerField.fsproj", "{26CB8141-0925-4A3C-A4FE-3190342EB85C}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LineUp", "practice\line-up\LineUp.fsproj", "{8DEE2219-A358-4153-B1E2-73C7A4742C5E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -916,6 +918,18 @@ Global
{74A88515-F7D5-447D-90B3-AD7EAF26549C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74A88515-F7D5-447D-90B3-AD7EAF26549C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74A88515-F7D5-447D-90B3-AD7EAF26549C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|x64.Build.0 = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Debug|x86.Build.0 = Debug|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|x64.ActiveCfg = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|x64.Build.0 = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|x86.ActiveCfg = Release|Any CPU
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B404AA3C-A226-409A-A035-6C1DC66940DD} = {B7E719DB-FB8D-43B4-B529-55FCF6E3DC3F}
@@ -1068,5 +1082,6 @@ Global
{FF432193-3D08-4BD4-ADCD-1512BF67A425} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{74A88515-F7D5-447D-90B3-AD7EAF26549C} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{26CB8141-0925-4A3C-A4FE-3190342EB85C} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
+ {8DEE2219-A358-4153-B1E2-73C7A4742C5E} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
EndGlobalSection
EndGlobal
diff --git a/exercises/practice/line-up/.config/dotnet-tools.json b/exercises/practice/line-up/.config/dotnet-tools.json
new file mode 100644
index 000000000..59f976062
--- /dev/null
+++ b/exercises/practice/line-up/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "fantomas": {
+ "version": "6.3.15",
+ "commands": [
+ "fantomas"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/exercises/practice/line-up/.docs/instructions.md b/exercises/practice/line-up/.docs/instructions.md
new file mode 100644
index 000000000..9e686ecbf
--- /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 000000000..ea07268ae
--- /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/Example.fs b/exercises/practice/line-up/.meta/Example.fs
new file mode 100644
index 000000000..7a65de48b
--- /dev/null
+++ b/exercises/practice/line-up/.meta/Example.fs
@@ -0,0 +1,14 @@
+module LineUp
+
+let suffix (number: int): string =
+ match number % 100 with
+ | 11 | 12 | 13 -> "th"
+ | _ ->
+ match number % 10 with
+ | 1 -> "st"
+ | 2 -> "nd"
+ | 3 -> "rd"
+ | _ -> "th"
+
+let format (name: string) (number: int): string =
+ sprintf "%s, you are the %d%s customer we serve today. Thank you!" name number (suffix number)
\ No newline at end of file
diff --git a/exercises/practice/line-up/.meta/config.json b/exercises/practice/line-up/.meta/config.json
new file mode 100644
index 000000000..4f2f63997
--- /dev/null
+++ b/exercises/practice/line-up/.meta/config.json
@@ -0,0 +1,22 @@
+{
+ "authors": [
+ "BNAndras"
+ ],
+ "files": {
+ "solution": [
+ "LineUp.fs"
+ ],
+ "test": [
+ "LineUpTests.fs"
+ ],
+ "example": [
+ ".meta/Example.fs"
+ ],
+ "invalidator": [
+ "LineUp.fsproj"
+ ]
+ },
+ "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/tests.toml b/exercises/practice/line-up/.meta/tests.toml
new file mode 100644
index 000000000..36fdf1d0c
--- /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/LineUp.fs b/exercises/practice/line-up/LineUp.fs
new file mode 100644
index 000000000..e0ddf006a
--- /dev/null
+++ b/exercises/practice/line-up/LineUp.fs
@@ -0,0 +1,3 @@
+module LineUp
+
+let format (name: string) (number: int): string = failwith "You need to implement this function."
\ No newline at end of file
diff --git a/exercises/practice/line-up/LineUp.fsproj b/exercises/practice/line-up/LineUp.fsproj
new file mode 100644
index 000000000..dc7bb89b7
--- /dev/null
+++ b/exercises/practice/line-up/LineUp.fsproj
@@ -0,0 +1,21 @@
+
+
+
+ net9.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/exercises/practice/line-up/LineUpTests.fs b/exercises/practice/line-up/LineUpTests.fs
new file mode 100644
index 000000000..c2cd4c957
--- /dev/null
+++ b/exercises/practice/line-up/LineUpTests.fs
@@ -0,0 +1,83 @@
+module LineUpTests
+
+open FsUnit.Xunit
+open Xunit
+
+open LineUp
+
+[]
+let ``Format smallest non-exceptional ordinal numeral 4`` () =
+ format "Gianna" 4 |> should equal "Gianna, you are the 4th customer we serve today. Thank you!"
+
+[]
+let ``Format greatest single digit non-exceptional ordinal numeral 9`` () =
+ format "Maarten" 9 |> should equal "Maarten, you are the 9th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 5`` () =
+ format "Petronila" 5 |> should equal "Petronila, you are the 5th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 6`` () =
+ format "Attakullakulla" 6 |> should equal "Attakullakulla, you are the 6th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 7`` () =
+ format "Kate" 7 |> should equal "Kate, you are the 7th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 8`` () =
+ format "Maximiliano" 8 |> should equal "Maximiliano, you are the 8th customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 1`` () =
+ format "Mary" 1 |> should equal "Mary, you are the 1st customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 2`` () =
+ format "Haruto" 2 |> should equal "Haruto, you are the 2nd customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 3`` () =
+ format "Henriette" 3 |> should equal "Henriette, you are the 3rd customer we serve today. Thank you!"
+
+[]
+let ``Format smallest two digit non-exceptional ordinal numeral 10`` () =
+ format "Alvarez" 10 |> should equal "Alvarez, you are the 10th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 11`` () =
+ format "Jacqueline" 11 |> should equal "Jacqueline, you are the 11th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 12`` () =
+ format "Juan" 12 |> should equal "Juan, you are the 12th customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 13`` () =
+ format "Patricia" 13 |> should equal "Patricia, you are the 13th customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 21`` () =
+ format "Washi" 21 |> should equal "Washi, you are the 21st customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 62`` () =
+ format "Nayra" 62 |> should equal "Nayra, you are the 62nd customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 100`` () =
+ format "John" 100 |> should equal "John, you are the 100th customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 101`` () =
+ format "Zeinab" 101 |> should equal "Zeinab, you are the 101st customer we serve today. Thank you!"
+
+[]
+let ``Format non-exceptional ordinal numeral 112`` () =
+ format "Knud" 112 |> should equal "Knud, you are the 112th customer we serve today. Thank you!"
+
+[]
+let ``Format exceptional ordinal numeral 123`` () =
+ format "Yma" 123 |> should equal "Yma, you are the 123rd customer we serve today. Thank you!"
+
diff --git a/generators/Generators.fs b/generators/Generators.fs
index e999d1000..59fccf3f4 100644
--- a/generators/Generators.fs
+++ b/generators/Generators.fs
@@ -911,6 +911,9 @@ type LargestSeriesProduct() =
type Leap() =
inherit ExerciseGenerator()
+type LineUp() =
+ inherit ExerciseGenerator()
+
type ListOps() =
inherit ExerciseGenerator()