diff --git a/docs/exercises.md b/docs/exercises.md
index 7a88f72..03d40d8 100644
--- a/docs/exercises.md
+++ b/docs/exercises.md
@@ -49,6 +49,14 @@ title: Exercises
25_range_iterators - Range over Iterators
26_errors - Error Handling
27_custom_errors - Custom Errors
+ 28_defer - Defer
+ 29_go_routines - Go Routines
+ 30_channels - Channels
+ 31_mutexes - Mutexes
+ 32_sorting - Sorting
+ 33_string_formatting - String Formatting
+ 34_channel_buffering - Channel Buffering
+ 41_regex - Regular Expressions
36_json - JSON Processing
37_xml - XML Processing
diff --git a/internal/exercises/catalog.yaml b/internal/exercises/catalog.yaml
index 37b779b..be980eb 100644
--- a/internal/exercises/catalog.yaml
+++ b/internal/exercises/catalog.yaml
@@ -169,6 +169,11 @@ concepts:
test_regex: ".*"
hints:
- Use `make(chan T, N)` to create a buffered channel and `<-` to send and receive on it.
+- slug: 35_channel_sync
+ title: Channel Synchronization
+ test_regex: ".*"
+ hints:
+ - Use a buffered boolean channel and wait till go routine completes.
- slug: 39_channel_directions
title: Channel Directions
test_regex: ".*"
@@ -178,11 +183,18 @@ concepts:
- Send 50 jobs to the worker pool.
- Receive results from the worker pool and store them in a `rs` slice.
- Enforce type safety by specifying the channel directions in worker function.
-- slug: 35_channel_sync
- title: Channel Synchronization
- test_regex: ".*"
- hints:
- - Use a buffered boolean channel and wait till go routine completes.
+- slug: 41_regex
+ title: Regular Expressions
+ test_regex: ".*"
+ hints:
+ - Use regexp.MustCompile to create compiled regular expressions.
+ - Use MatchString to test if a string matches a pattern.
+ - Use FindAllString to extract all matches from text.
+ - Use ReplaceAllString to replace matches with new text.
+ - "Email regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
+ - "Phone regex: ^\\(\\d{3}\\) \\d{3}-\\d{4}$"
+ - "Number regex: \\d+\\.?\\d*"
+ - "Vowel regex: [aeiouAEIOU]"
- slug: 37_xml
title: XML Encoding and Decoding
test_regex: ".*"
diff --git a/internal/exercises/solutions/41_regex/regex.go b/internal/exercises/solutions/41_regex/regex.go
new file mode 100644
index 0000000..29895eb
--- /dev/null
+++ b/internal/exercises/solutions/41_regex/regex.go
@@ -0,0 +1,35 @@
+package regex
+
+import "regexp"
+
+// IsValidEmail returns true if the email address is valid.
+func IsValidEmail(email string) bool {
+ pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
+ re := regexp.MustCompile(pattern)
+ return re.MatchString(email)
+}
+
+// ExtractNumbers returns all numbers found in the input string.
+func ExtractNumbers(text string) []string {
+ pattern := `\d+\.?\d*`
+ re := regexp.MustCompile(pattern)
+ matches := re.FindAllString(text, -1)
+ if matches == nil {
+ return []string{}
+ }
+ return matches
+}
+
+// ReplaceVowels replaces all vowels with asterisks.
+func ReplaceVowels(text string) string {
+ pattern := `[aeiouAEIOU]`
+ re := regexp.MustCompile(pattern)
+ return re.ReplaceAllString(text, "*")
+}
+
+// IsPhoneNumber returns true if the phone number matches format (XXX) XXX-XXXX.
+func IsPhoneNumber(phone string) bool {
+ pattern := `^\(\d{3}\) \d{3}-\d{4}$`
+ re := regexp.MustCompile(pattern)
+ return re.MatchString(phone)
+}
diff --git a/internal/exercises/templates/41_regex/regex.go b/internal/exercises/templates/41_regex/regex.go
new file mode 100644
index 0000000..d53b161
--- /dev/null
+++ b/internal/exercises/templates/41_regex/regex.go
@@ -0,0 +1,37 @@
+package regex
+
+// Task:
+// Implement regular expression functions using Go's regexp package.
+//
+// 1. Implement IsValidEmail to validate email addresses using regex.
+// 2. Implement ExtractNumbers to extract all numbers from a string.
+// 3. Implement ReplaceVowels to replace all vowels with asterisks.
+// 4. Implement IsPhoneNumber to validate phone numbers in format (XXX) XXX-XXXX.
+
+// IsValidEmail should return true if the email address is valid.
+// A valid email should have the format: user@domain.com
+func IsValidEmail(email string) bool {
+ // TODO: implement using regexp.MustCompile and MatchString
+ return false
+}
+
+// ExtractNumbers should return all numbers found in the input string.
+// Numbers can be integers or decimals (e.g., "123", "45.67").
+func ExtractNumbers(text string) []string {
+ // TODO: implement using regexp.FindAllString
+ return nil
+}
+
+// ReplaceVowels should replace all vowels (a, e, i, o, u) with asterisks.
+// Case-insensitive replacement.
+func ReplaceVowels(text string) string {
+ // TODO: implement using regexp.MustCompile and ReplaceAllString
+ return ""
+}
+
+// IsPhoneNumber should return true if the phone number matches format (XXX) XXX-XXXX.
+// Example: (123) 456-7890
+func IsPhoneNumber(phone string) bool {
+ // TODO: implement using regexp.MustCompile and MatchString
+ return false
+}
diff --git a/internal/exercises/templates/41_regex/regex_test.go b/internal/exercises/templates/41_regex/regex_test.go
new file mode 100644
index 0000000..087b45a
--- /dev/null
+++ b/internal/exercises/templates/41_regex/regex_test.go
@@ -0,0 +1,99 @@
+package regex
+
+import "testing"
+
+func TestIsValidEmail(t *testing.T) {
+ tests := []struct {
+ email string
+ expected bool
+ }{
+ {"user@domain.com", true},
+ {"test.email@example.org", true},
+ {"invalid-email", false},
+ {"@domain.com", false},
+ {"user@", false},
+ {"user@domain", false},
+ {"", false},
+ }
+
+ for _, test := range tests {
+ result := IsValidEmail(test.email)
+ if result != test.expected {
+ t.Errorf("IsValidEmail(%q) = %v, want %v", test.email, result, test.expected)
+ }
+ }
+}
+
+func TestExtractNumbers(t *testing.T) {
+ tests := []struct {
+ text string
+ expected []string
+ }{
+ {"I have 123 apples and 45.67 oranges", []string{"123", "45.67"}},
+ {"The price is $99.99", []string{"99.99"}},
+ {"No numbers here", []string{}},
+ {"123", []string{"123"}},
+ {"12.34.56", []string{"12.34", "56"}},
+ {"", []string{}},
+ }
+
+ for _, test := range tests {
+ result := ExtractNumbers(test.text)
+ if len(result) != len(test.expected) {
+ t.Errorf("ExtractNumbers(%q) length = %d, want %d", test.text, len(result), len(test.expected))
+ continue
+ }
+ for i, num := range result {
+ if num != test.expected[i] {
+ t.Errorf("ExtractNumbers(%q)[%d] = %q, want %q", test.text, i, num, test.expected[i])
+ }
+ }
+ }
+}
+
+func TestReplaceVowels(t *testing.T) {
+ tests := []struct {
+ text string
+ expected string
+ }{
+ {"hello", "h*ll*"},
+ {"HELLO", "H*LL*"},
+ {"Hello World", "H*ll* W*rld"},
+ {"bcdfg", "bcdfg"},
+ {"", ""},
+ {"aeiou", "*****"},
+ {"AEIOU", "*****"},
+ }
+
+ for _, test := range tests {
+ result := ReplaceVowels(test.text)
+ if result != test.expected {
+ t.Errorf("ReplaceVowels(%q) = %q, want %q", test.text, result, test.expected)
+ }
+ }
+}
+
+func TestIsPhoneNumber(t *testing.T) {
+ tests := []struct {
+ phone string
+ expected bool
+ }{
+ {"(123) 456-7890", true},
+ {"(555) 123-4567", true},
+ {"(000) 000-0000", true},
+ {"123-456-7890", false},
+ {"(123)456-7890", false},
+ {"(123) 4567890", false},
+ {"123 456 7890", false},
+ {"(123) 456-789", false},
+ {"(12) 456-7890", false},
+ {"", false},
+ }
+
+ for _, test := range tests {
+ result := IsPhoneNumber(test.phone)
+ if result != test.expected {
+ t.Errorf("IsPhoneNumber(%q) = %v, want %v", test.phone, result, test.expected)
+ }
+ }
+}