diff --git a/config.json b/config.json index df37a59b1..2c741a627 100644 --- a/config.json +++ b/config.json @@ -2744,6 +2744,21 @@ "classes" ], "difficulty": 5 + }, + { + "slug": "baffling-birthdays", + "name": "baffling-birthdays", + "uuid": "f75d1dd2-63ee-4258-a4e9-f208fb8e8bd4", + "practices": [ + "randomness", + "datetimes" + ], + "prerequisites": [ + "datetimes", + "randomness", + "numbers" + ], + "difficulty": 6 } ], "foregone": [ diff --git a/exercises/Exercises.sln b/exercises/Exercises.sln index 08e9a3d9c..bb2050a70 100644 --- a/exercises/Exercises.sln +++ b/exercises/Exercises.sln @@ -361,6 +361,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntergalacticTransmission", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SplitSecondStopwatch", "practice\split-second-stopwatch\SplitSecondStopwatch.csproj", "{0A6CD51A-2120-4A72-A17B-08CCC2E6D365}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BafflingBirthdays", "practice\baffling-birthdays\BafflingBirthdays.csproj", "{1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -2495,6 +2497,18 @@ Global {0A6CD51A-2120-4A72-A17B-08CCC2E6D365}.Release|x64.Build.0 = Release|Any CPU {0A6CD51A-2120-4A72-A17B-08CCC2E6D365}.Release|x86.ActiveCfg = Release|Any CPU {0A6CD51A-2120-4A72-A17B-08CCC2E6D365}.Release|x86.Build.0 = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|x64.ActiveCfg = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|x64.Build.0 = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|x86.ActiveCfg = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Debug|x86.Build.0 = Debug|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|Any CPU.Build.0 = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x64.ActiveCfg = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x64.Build.0 = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x86.ActiveCfg = Release|Any CPU + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2677,6 +2691,7 @@ Global {6BD384E6-225E-4F8A-856C-3079957C6E36} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} {E81F1BA3-1F99-4DCB-B875-78D1F4750BD5} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} {0A6CD51A-2120-4A72-A17B-08CCC2E6D365} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} + {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB4EA6C9-5461-4024-BDC7-2AE0C3A85CD1} diff --git a/exercises/practice/baffling-birthdays/.docs/instructions.md b/exercises/practice/baffling-birthdays/.docs/instructions.md new file mode 100644 index 000000000..a01ec8679 --- /dev/null +++ b/exercises/practice/baffling-birthdays/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Your task is to estimate the birthday paradox's probabilities. + +To do this, you need to: + +- Generate random birthdates. +- Check if a collection of randomly generated birthdates contains at least two with the same birthday. +- Estimate the probability that at least two people in a group share the same birthday for different group sizes. + +~~~~exercism/note +A birthdate includes the full date of birth (year, month, and day), whereas a birthday refers only to the month and day, which repeat each year. +Two birthdates with the same month and day correspond to the same birthday. +~~~~ + +~~~~exercism/caution +The birthday paradox assumes that: + +- There are 365 possible birthdays (no leap years). +- Each birthday is equally likely (uniform distribution). + +Your implementation must follow these assumptions. +~~~~ diff --git a/exercises/practice/baffling-birthdays/.docs/introduction.md b/exercises/practice/baffling-birthdays/.docs/introduction.md new file mode 100644 index 000000000..97dabd1e6 --- /dev/null +++ b/exercises/practice/baffling-birthdays/.docs/introduction.md @@ -0,0 +1,25 @@ +# Introduction + +Fresh out of college, you're throwing a huge party to celebrate with friends and family. +Over 70 people have shown up, including your mildly eccentric Uncle Ted. + +In one of his usual antics, he bets you £100 that at least two people in the room share the same birthday. +That sounds ridiculous — there are many more possible birthdays than there are guests, so you confidently accept. + +To your astonishment, after collecting the birthdays of just 32 guests, you've already found two guests that share the same birthday. +Accepting your loss, you hand Uncle Ted his £100, but something feels off. + +The next day, curiosity gets the better of you. +A quick web search leads you to the [birthday paradox][birthday-problem], which reveals that with just 23 people, the probability of a shared birthday exceeds 50%. + +Ah. So _that's_ why Uncle Ted was so confident. + +Determined to turn the tables, you start looking up other paradoxes; next time, _you'll_ be the one making the bets. + +~~~~exercism/note +The birthday paradox is a [veridical paradox][veridical-paradox]: even though it feels wrong, it is actually true. + +[veridical-paradox]: https://en.wikipedia.org/wiki/Paradox#Quine's_classification +~~~~ + +[birthday-problem]: https://en.wikipedia.org/wiki/Birthday_problem diff --git a/exercises/practice/baffling-birthdays/.editorconfig b/exercises/practice/baffling-birthdays/.editorconfig new file mode 100644 index 000000000..dd5ce951d --- /dev/null +++ b/exercises/practice/baffling-birthdays/.editorconfig @@ -0,0 +1,141 @@ +############################### +# Core EditorConfig Options # +############################### + +; This file is for unifying the coding style for different editors and IDEs. +; More information at: +; https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017 +; https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2017 + +root = true + +[*] +indent_style = space + +[BafflingBirthdays.cs] +indent_size = 4 + +############################### +# .NET Coding Conventions # +############################### + +# Organize usings +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = true + +# this. preferences +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = always:suggestion +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +############################### +# Naming Conventions # +############################### + +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +############################### +# C# Code Style Rules # +############################### + +# var preferences +csharp_style_var_for_built_in_types = true:none +csharp_style_var_when_type_is_apparent = true:none +csharp_style_var_elsewhere = true:none + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion + +# Pattern-matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Expression-level preferences +csharp_prefer_braces = true:none +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +############################### +# C# Formatting Rules # +############################### + +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_members_in_anonymous_types = false +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true diff --git a/exercises/practice/baffling-birthdays/.meta/Example.cs b/exercises/practice/baffling-birthdays/.meta/Example.cs new file mode 100644 index 000000000..c43d775cd --- /dev/null +++ b/exercises/practice/baffling-birthdays/.meta/Example.cs @@ -0,0 +1,34 @@ +public static class BafflingBirthdays +{ + private static DateOnly RandomBirthdate(int year) + { + var month = Random.Shared.Next(1, 12 + 1); + var day = Random.Shared.Next(1, DateTime.DaysInMonth(year, month) + 1); + return new DateOnly(year, month, day); + } + + public static DateOnly[] RandomBirthdates(int numberOfBirthdays) + { + var year = Random.Shared.Next(1900, DateTime.Now.Year + 1); + return Enumerable.Range(0, numberOfBirthdays).Select(_ => RandomBirthdate(year)).ToArray(); + } + + public static bool SharedBirthday(DateOnly[] birthdays) => + birthdays + .SelectMany((birthday, i) => birthdays.Skip(i + 1), MatchingBirthday) + .Any(matching => matching); + + private static bool MatchingBirthday(DateOnly birthday1, DateOnly birthday2) => + birthday1.Month == birthday2.Month && birthday1.Day == birthday2.Day; + + public static double EstimatedProbabilityOfSharedBirthday(int numberOfBirthdays) + { + var matchingBirthdayIterationCount = 0; + for (var i = 0; i < 10000; i++) + { + if (SharedBirthday(RandomBirthdates(numberOfBirthdays))) + matchingBirthdayIterationCount++; + } + return matchingBirthdayIterationCount / 100.0; + } +} diff --git a/exercises/practice/baffling-birthdays/.meta/Generator.tpl b/exercises/practice/baffling-birthdays/.meta/Generator.tpl new file mode 100644 index 000000000..3dce9d85a --- /dev/null +++ b/exercises/practice/baffling-birthdays/.meta/Generator.tpl @@ -0,0 +1,50 @@ +{{ func to_dateonly + parts = string.split $0 "-" + year = parts[0] | string.to_int + month = parts[1] | string.to_int + day = parts[2] | string.to_int + $"new({year}, {month}, {day})" +end }} + +public class {{ testClass }} +{ + {{- for test in tests }} + [Fact{{ if !for.first }}(Skip = "Remove this Skip property to run this test"){{ end }}] + public void {{ test.testMethod }}() + { + {{- if test.property == "sharedBirthday" }} + DateOnly[] birthdates = [ + {{- for birthdate in test.input.birthdates }} + {{ birthdate | to_dateonly }}{{- if !for.last }},{{ end -}} + {{ end }} + ]; + Assert.{{ test.expected ? "True" : "False" }}({{ testedClass }}.{{ test.testedMethod }}(birthdates)); + {{- else if test.property == "randomBirthdates" }} + {{-if test.expected.years }} + var uniqueRandomYears = {{ testedClass }}.{{ test.testedMethod }}(50) + .Select(birthdate => birthdate.Year) + .ToHashSet(); + Assert.All(uniqueRandomYears, year => Assert.False(DateTime.IsLeapYear(year))); + {{- else if test.expected.months }} + var uniqueRandomMonths = {{ testedClass }}.{{ test.testedMethod }}(100) + .Select(birthdate => birthdate.Month) + .ToHashSet(); + Assert.Equal(12, uniqueRandomMonths.Count); + {{- else if test.expected.days }} + var uniqueRandomDays = {{ testedClass }}.{{ test.testedMethod }}(300) + .Select(birthdate => birthdate.Day) + .ToHashSet(); + Assert.Equal(31, uniqueRandomDays.Count); + {{- else }} + for (var count = 1; count < 10; count++) + { + var randomBirthdates = {{ testedClass }}.{{ test.testedMethod }}(count); + Assert.Equal(count, randomBirthdates.Length); + } + {{ end -}} + {{- else if test.property == "estimatedProbabilityOfSharedBirthday" }} + Assert.Equal({{ test.expected }}, {{ testedClass }}.{{ test.testedMethod }}({{ test.input.groupSize }}), tolerance: 1.0); + {{ end -}} + } + {{ end -}} +} diff --git a/exercises/practice/baffling-birthdays/.meta/config.json b/exercises/practice/baffling-birthdays/.meta/config.json new file mode 100644 index 000000000..9b54482ec --- /dev/null +++ b/exercises/practice/baffling-birthdays/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "erikschierboom" + ], + "files": { + "solution": [ + "BafflingBirthdays.cs" + ], + "test": [ + "BafflingBirthdaysTests.cs" + ], + "example": [ + ".meta/Example.cs" + ], + "invalidator": [ + "BafflingBirthdays.csproj" + ] + }, + "blurb": "Estimate the birthday paradox's probabilities.", + "source": "Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/pull/2539" +} diff --git a/exercises/practice/baffling-birthdays/.meta/tests.toml b/exercises/practice/baffling-birthdays/.meta/tests.toml new file mode 100644 index 000000000..c76afb466 --- /dev/null +++ b/exercises/practice/baffling-birthdays/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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. + +[716dcc2b-8fe4-4fc9-8c48-cbe70d8e6b67] +description = "shared birthday -> one birthdate" + +[f7b3eb26-bcfc-4c1e-a2de-af07afc33f45] +description = "shared birthday -> two birthdates with same year, month, and day" + +[7193409a-6e16-4bcb-b4cc-9ffe55f79b25] +description = "shared birthday -> two birthdates with same year and month, but different day" + +[d04db648-121b-4b72-93e8-d7d2dced4495] +description = "shared birthday -> two birthdates with same month and day, but different year" + +[3c8bd0f0-14c6-4d4c-975a-4c636bfdc233] +description = "shared birthday -> two birthdates with same year, but different month and day" + +[df5daba6-0879-4480-883c-e855c99cdaa3] +description = "shared birthday -> two birthdates with different year, month, and day" + +[0c17b220-cbb9-4bd7-872f-373044c7b406] +description = "shared birthday -> multiple birthdates without shared birthday" + +[966d6b0b-5c0a-4b8c-bc2d-64939ada49f8] +description = "shared birthday -> multiple birthdates with one shared birthday" + +[b7937d28-403b-4500-acce-4d9fe3a9620d] +description = "shared birthday -> multiple birthdates with more than one shared birthday" + +[70b38cea-d234-4697-b146-7d130cd4ee12] +description = "random birthdates -> generate requested number of birthdates" + +[d9d5b7d3-5fea-4752-b9c1-3fcd176d1b03] +description = "random birthdates -> years are not leap years" + +[d1074327-f68c-4c8a-b0ff-e3730d0f0521] +description = "random birthdates -> months are random" + +[7df706b3-c3f5-471d-9563-23a4d0577940] +description = "random birthdates -> days are random" + +[89a462a4-4265-4912-9506-fb027913f221] +description = "estimated probability of at least one shared birthday -> for one person" + +[ec31c787-0ebb-4548-970c-5dcb4eadfb5f] +description = "estimated probability of at least one shared birthday -> among ten people" + +[b548afac-a451-46a3-9bb0-cb1f60c48e2f] +description = "estimated probability of at least one shared birthday -> among twenty-three people" + +[e43e6b9d-d77b-4f6c-a960-0fc0129a0bc5] +description = "estimated probability of at least one shared birthday -> among seventy people" diff --git a/exercises/practice/baffling-birthdays/BafflingBirthdays.cs b/exercises/practice/baffling-birthdays/BafflingBirthdays.cs new file mode 100644 index 000000000..7a20d5a4d --- /dev/null +++ b/exercises/practice/baffling-birthdays/BafflingBirthdays.cs @@ -0,0 +1,17 @@ +public static class BafflingBirthdays +{ + public static DateOnly[] RandomBirthdates(int numberOfBirthdays) + { + throw new NotImplementedException("You need to implement this method."); + } + + public static bool SharedBirthday(DateOnly[] birthdays) + { + throw new NotImplementedException("You need to implement this method."); + } + + public static double EstimatedProbabilityOfSharedBirthday(int numberOfBirthdays) + { + throw new NotImplementedException("You need to implement this method."); + } +} diff --git a/exercises/practice/baffling-birthdays/BafflingBirthdays.csproj b/exercises/practice/baffling-birthdays/BafflingBirthdays.csproj new file mode 100644 index 000000000..760699d32 --- /dev/null +++ b/exercises/practice/baffling-birthdays/BafflingBirthdays.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + Exe + enable + enable + true + + + + + + + + + + + + + + diff --git a/exercises/practice/baffling-birthdays/BafflingBirthdaysTests.cs b/exercises/practice/baffling-birthdays/BafflingBirthdaysTests.cs new file mode 100644 index 000000000..8e122ec24 --- /dev/null +++ b/exercises/practice/baffling-birthdays/BafflingBirthdaysTests.cs @@ -0,0 +1,159 @@ +public class BafflingBirthdaysTests +{ + [Fact] + public void Shared_birthday_one_birthdate() + { + DateOnly[] birthdates = [ + new(2000, 1, 1) + ]; + Assert.False(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_two_birthdates_with_same_year_month_and_day() + { + DateOnly[] birthdates = [ + new(2000, 1, 1), + new(2000, 1, 1) + ]; + Assert.True(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_two_birthdates_with_same_year_and_month_but_different_day() + { + DateOnly[] birthdates = [ + new(2012, 5, 9), + new(2012, 5, 17) + ]; + Assert.False(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_two_birthdates_with_same_month_and_day_but_different_year() + { + DateOnly[] birthdates = [ + new(1999, 10, 23), + new(1988, 10, 23) + ]; + Assert.True(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_two_birthdates_with_same_year_but_different_month_and_day() + { + DateOnly[] birthdates = [ + new(2007, 12, 19), + new(2007, 4, 27) + ]; + Assert.False(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_two_birthdates_with_different_year_month_and_day() + { + DateOnly[] birthdates = [ + new(1997, 8, 4), + new(1963, 11, 23) + ]; + Assert.False(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_multiple_birthdates_without_shared_birthday() + { + DateOnly[] birthdates = [ + new(1966, 7, 29), + new(1977, 2, 12), + new(2001, 12, 25), + new(1980, 11, 10) + ]; + Assert.False(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_multiple_birthdates_with_one_shared_birthday() + { + DateOnly[] birthdates = [ + new(1966, 7, 29), + new(1977, 2, 12), + new(2001, 7, 29), + new(1980, 11, 10) + ]; + Assert.True(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Shared_birthday_multiple_birthdates_with_more_than_one_shared_birthday() + { + DateOnly[] birthdates = [ + new(1966, 7, 29), + new(1977, 2, 12), + new(2001, 12, 25), + new(1980, 7, 29), + new(2019, 2, 12) + ]; + Assert.True(BafflingBirthdays.SharedBirthday(birthdates)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Random_birthdates_generate_requested_number_of_birthdates() + { + for (var count = 1; count < 10; count++) + { + var randomBirthdates = BafflingBirthdays.RandomBirthdates(count); + Assert.Equal(count, randomBirthdates.Length); + } + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Random_birthdates_years_are_not_leap_years() + { + var uniqueRandomYears = BafflingBirthdays.RandomBirthdates(50) + .Select(birthdate => birthdate.Year) + .ToHashSet(); + Assert.All(uniqueRandomYears, year => Assert.False(DateTime.IsLeapYear(year))); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Random_birthdates_months_are_random() + { + var uniqueRandomMonths = BafflingBirthdays.RandomBirthdates(100) + .Select(birthdate => birthdate.Month) + .ToHashSet(); + Assert.Equal(12, uniqueRandomMonths.Count); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Random_birthdates_days_are_random() + { + var uniqueRandomDays = BafflingBirthdays.RandomBirthdates(300) + .Select(birthdate => birthdate.Day) + .ToHashSet(); + Assert.Equal(31, uniqueRandomDays.Count); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Estimated_probability_of_at_least_one_shared_birthday_for_one_person() + { + Assert.Equal(0.0, BafflingBirthdays.EstimatedProbabilityOfSharedBirthday(1), tolerance: 1.0); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Estimated_probability_of_at_least_one_shared_birthday_among_ten_people() + { + Assert.Equal(11.694818, BafflingBirthdays.EstimatedProbabilityOfSharedBirthday(10), tolerance: 1.0); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Estimated_probability_of_at_least_one_shared_birthday_among_twenty_three_people() + { + Assert.Equal(50.729723, BafflingBirthdays.EstimatedProbabilityOfSharedBirthday(23), tolerance: 1.0); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Estimated_probability_of_at_least_one_shared_birthday_among_seventy_people() + { + Assert.Equal(99.915958, BafflingBirthdays.EstimatedProbabilityOfSharedBirthday(70), tolerance: 1.0); + } +} diff --git a/exercises/practice/baffling-birthdays/packages.lock.json b/exercises/practice/baffling-birthdays/packages.lock.json new file mode 100644 index 000000000..6b3387771 --- /dev/null +++ b/exercises/practice/baffling-birthdays/packages.lock.json @@ -0,0 +1,170 @@ +{ + "version": 1, + "dependencies": { + "net9.0": { + "Exercism.Tests.xunit.v3": { + "type": "Direct", + "requested": "[0.1.0-beta1, )", + "resolved": "0.1.0-beta1", + "contentHash": "XjVtQWWxmHDDj7UMdkPKpBFFKnsW0tkBhlyJSfFFh+fWwGemyyJwJYhdsvWhiKKCY7zItB+mI/o0OQtOKQxUhA==", + "dependencies": { + "xunit.v3.extensibility.core": "1.1.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.12.0, )", + "resolved": "17.12.0", + "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==", + "dependencies": { + "Microsoft.CodeCoverage": "17.12.0", + "Microsoft.TestPlatform.TestHost": "17.12.0" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "lbyYtsBxA8Pz8kaf5Xn/Mj1mL9z2nlBWdZhqFaj66nxXBa4JwiTDm4eGcpSMet6du9TOWI6bfha+gQR6+IHawg==" + }, + "xunit.v3": { + "type": "Direct", + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "1ckSz5GVswlM9TCk5bGdHOjnYwqAWjkeqxckoHawQIA8sTeuN+RCBUypCi5A/Um0XlczRx5TjAK5W6BbN0HLcQ==", + "dependencies": { + "xunit.analyzers": "1.20.0", + "xunit.v3.assert": "[1.1.0]", + "xunit.v3.core": "[1.1.0]" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==" + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "h34zKNpGyni66VH738mRHeXSnf3klSShUdavUWNhSfWICUUi5aXeI0LBvoX/ad93N0+9xBDU3Fyi6WfxrwKQGw==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "WqJydnJ99dEKtquR9HwINz104ehWJKTXbQQrydGatlLRw14bmsx0pa8+E6KUXMYXZAimN0swWlDmcJGjjW4TIg==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "bOtpRMSPeT5YLQo+NNY8EtdNTphAUcmALjW4ABU7P0rb6yR2XAZau3TzNieLmR3lRuwudguWzzBhgcLRXwZh0A==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.12.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.20.0", + "contentHash": "HElev2E9vFbPxwKRQtpCSSzLOu8M/N9EWBCB37v7SRx6z4Lbj19FxfLEig3v9jiI6s4b0l2uena91nEsTWl9jA==" + }, + "xunit.v3.assert": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "4D+eM08ImfhA+zLbRzi8HA4qsT98zDxgaCD7vCg8yFesokKsgSsqWsAmImHFjVymGVhVS7WFGb19d6v1k9i0xQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0", + "System.Memory": "4.6.0" + } + }, + "xunit.v3.common": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "Cq55z8pC7fOkfj+3TB/YQ6OW96qWqxKiMd15CtkIl37VtV9EsiUL4B4HsR6VLJCzkk7cBiXQ1ABVIcp3TCm6HQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit.v3.core": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kXP/1d3jnQ2m4skcdM3gSMmubI6P747D6KVswzeedysgFkLj2xJlfo7p7slsmtEnp8BZb8X6D92Hssd/UtVPMw==", + "dependencies": { + "Microsoft.Testing.Platform.MSBuild": "1.5.3", + "xunit.v3.extensibility.core": "[1.1.0]", + "xunit.v3.runner.inproc.console": "[1.1.0]" + } + }, + "xunit.v3.extensibility.core": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "AeQbbYN001x0c+B9pqwml6jZPovHz8O/sOp7jmrjz90rUzz/QPal12SlHLKYszR44CMnW4MsDam3RYT5pkYUxw==", + "dependencies": { + "xunit.v3.common": "[1.1.0]" + } + }, + "xunit.v3.runner.common": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "Q81J0VPuu8fpF+/1CIjThqKKUjnqh0TQrLlD0iORkF75KdsOV+iGWT8c3AVuY96kDoxXxkTf0ZvJsK6o9osc1A==", + "dependencies": { + "xunit.v3.common": "[1.1.0]" + } + }, + "xunit.v3.runner.inproc.console": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "lX/4TwIJe9ysCd5dqLk/Doq8ieYaZGivgf95xR59wRuSV+nHzHnyhpjXfaPUp8nkncUH1rOmJ85o1KebipisXQ==", + "dependencies": { + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.5.3", + "Microsoft.Testing.Platform": "1.5.3", + "xunit.v3.extensibility.core": "[1.1.0]", + "xunit.v3.runner.common": "[1.1.0]" + } + } + } + } +} \ No newline at end of file