mirror of
https://github.com/harivansh-afk/veet-code.git
synced 2026-04-15 09:01:18 +00:00
update generate files and problem examples to be consistent
This commit is contained in:
parent
186bb75289
commit
933b466c25
2 changed files with 342 additions and 255 deletions
|
|
@ -1,83 +1,54 @@
|
||||||
---
|
---
|
||||||
description: Generate a new practice problem with tests
|
description: Generate a new practice problem with tests
|
||||||
argument-hint: [difficulty] [topic]
|
argument-hint: [difficulty] [topic]
|
||||||
allowed-tools: Write, Bash(mkdir:*)
|
allowed-tools: Write, Bash(mkdir:*), Bash(cd:*), Bash(python:*), Bash(rm:*)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Generate a New Practice Problem
|
# Generate a New Practice Problem
|
||||||
|
|
||||||
**Reference**: @.claude/problem-examples.md for consistent formatting and quality examples.
|
**Reference**: @.claude/problem-examples.md for consistent formatting and quality examples.
|
||||||
|
|
||||||
You are generating a practical implementation problem for veetcode - a terminal-based coding practice tool.
|
You are generating a practical implementation problem for veetcode.
|
||||||
|
|
||||||
## Arguments Provided
|
## Arguments Provided
|
||||||
- Difficulty: $1 (if empty, ask user to choose: easy, medium, or hard)
|
- Difficulty: $1 (if empty, ask user to choose: easy, medium, or hard)
|
||||||
- Topic: $2 (if empty, ask user what concept/topic they want to practice)
|
- Topic: $2 (if empty, ask user what concept/topic they want to practice)
|
||||||
|
|
||||||
## Problem Style Guide
|
## Generation Workflow
|
||||||
|
|
||||||
Generate **practical implementation** problems, NOT abstract LeetCode puzzles:
|
### Step 1: Gather Requirements
|
||||||
|
If arguments not provided, ask user:
|
||||||
|
1. Difficulty: easy, medium, or hard
|
||||||
|
2. Topic/concept they want to practice
|
||||||
|
3. Confirm with a one-line problem summary before proceeding
|
||||||
|
|
||||||
### DO:
|
### Step 2: Design the Problem (in your head)
|
||||||
- Use real-world business context (e.g., "You are building a payment system...")
|
Before writing any files, mentally design:
|
||||||
- Provide clear function signatures with type hints
|
- The problem scenario (real-world context)
|
||||||
- Include 2-3 concrete examples with explanations
|
- Function signature with types
|
||||||
- List explicit constraints
|
- The ACTUAL SOLUTION (keep this secret - never write to files)
|
||||||
- Focus on practical skills: data transformation, validation, API-like operations
|
- Test cases that verify the solution
|
||||||
|
- Edge cases
|
||||||
|
|
||||||
### DON'T:
|
### Step 3: Create Directory
|
||||||
- Create pure algorithmic puzzles without context
|
|
||||||
- Use abstract mathematical framing
|
|
||||||
- Make problems that feel like textbook exercises
|
|
||||||
|
|
||||||
## Difficulty Calibration
|
|
||||||
|
|
||||||
**Easy** (15-25 min):
|
|
||||||
- Single data structure (list, dict, set)
|
|
||||||
- 1-2 core concepts
|
|
||||||
- 3-4 test cases
|
|
||||||
- ~20-30 lines solution
|
|
||||||
|
|
||||||
**Medium** (30-40 min):
|
|
||||||
- Multiple data structures
|
|
||||||
- 3-4 concepts (sorting, hash maps, two pointers)
|
|
||||||
- 5-6 test cases including edge cases
|
|
||||||
- ~40-60 lines solution
|
|
||||||
|
|
||||||
**Hard** (45-60 min):
|
|
||||||
- Custom classes or complex data structures
|
|
||||||
- 5+ concepts (DP, graphs, sliding window + state)
|
|
||||||
- 7-10 test cases with tricky edge cases
|
|
||||||
- ~80+ lines solution
|
|
||||||
|
|
||||||
## Output Format
|
|
||||||
|
|
||||||
### 1. First, confirm with the user:
|
|
||||||
- The difficulty level
|
|
||||||
- The topic/concept
|
|
||||||
- A one-line problem summary
|
|
||||||
|
|
||||||
### 2. Generate the problem name
|
|
||||||
Create a kebab-case name (e.g., `validate-transactions`, `rate-limiter`, `word-frequency`)
|
|
||||||
|
|
||||||
### 3. Create the directory
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p problems/{difficulty}/{problem-name}
|
mkdir -p problems/{difficulty}/{problem-name}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Create solution.py
|
### Step 4: Write solution.py (SKELETON ONLY)
|
||||||
|
Write ONLY the skeleton - never include the solution!
|
||||||
|
|
||||||
Structure:
|
|
||||||
```python
|
```python
|
||||||
"""
|
"""
|
||||||
{Problem Title}
|
{Problem Title}
|
||||||
|
|
||||||
{Story-based description in 2-3 sentences with real-world context}
|
{Story-based description in 2-3 sentences with real-world context.
|
||||||
|
Frame it as a real task: "You're building...", "Your team needs...", etc.}
|
||||||
|
|
||||||
Example 1:
|
Example 1:
|
||||||
Input: {param1} = {value1}, {param2} = {value2}
|
Input: {param1} = {value1}, {param2} = {value2}
|
||||||
Output: {expected}
|
Output: {expected}
|
||||||
Explanation: {why this is the answer}
|
Explanation: {brief explanation}
|
||||||
|
|
||||||
Example 2:
|
Example 2:
|
||||||
Input: {param1} = {value1}, {param2} = {value2}
|
Input: {param1} = {value1}, {param2} = {value2}
|
||||||
|
|
@ -90,59 +61,148 @@ Constraints:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def {function_name}({params_with_types}) -> {return_type}:
|
def {function_name}({params}: {types}) -> {return_type}:
|
||||||
"""{One-line docstring describing what to return}."""
|
"""{One-line description of what to return}."""
|
||||||
pass # Your implementation here
|
pass # Your implementation here
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Create tests.py
|
### Step 5: Write tests.py (CONSISTENT FORMAT)
|
||||||
|
|
||||||
|
Follow this EXACT format for all tests:
|
||||||
|
|
||||||
Structure:
|
|
||||||
```python
|
```python
|
||||||
|
"""Tests for {problem_name}."""
|
||||||
import pytest
|
import pytest
|
||||||
from solution import {function_name}
|
from solution import {function_name}
|
||||||
|
|
||||||
|
|
||||||
def test_basic_case():
|
class TestBasicCases:
|
||||||
"""Test the example from the problem description."""
|
"""Test basic functionality with typical inputs."""
|
||||||
assert {function_name}(...) == ...
|
|
||||||
|
def test_example_one(self):
|
||||||
|
"""Test first example from problem description."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
def test_example_two(self):
|
||||||
|
"""Test second example from problem description."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
def test_typical_case(self):
|
||||||
|
"""Test another common case."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
|
||||||
def test_another_case():
|
class TestEdgeCases:
|
||||||
"""Test another typical case."""
|
"""Test edge cases and boundary conditions."""
|
||||||
assert {function_name}(...) == ...
|
|
||||||
|
def test_empty_input(self):
|
||||||
|
"""Test with empty or minimal input."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
def test_single_element(self):
|
||||||
|
"""Test with single element input."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
def test_boundary_values(self):
|
||||||
|
"""Test boundary conditions."""
|
||||||
|
assert {function_name}(...) == ...
|
||||||
|
|
||||||
|
|
||||||
def test_edge_case_empty():
|
# Test count by difficulty:
|
||||||
"""Test empty or minimal input."""
|
# Easy: 4-5 tests (2 basic, 2-3 edge)
|
||||||
assert {function_name}(...) == ...
|
# Medium: 6-8 tests (3 basic, 3-5 edge)
|
||||||
|
# Hard: 8-12 tests (4 basic, 4-8 edge)
|
||||||
|
|
||||||
def test_edge_case_boundary():
|
|
||||||
"""Test boundary conditions."""
|
|
||||||
assert {function_name}(...) == ...
|
|
||||||
|
|
||||||
|
|
||||||
# Add more tests based on difficulty:
|
|
||||||
# Easy: 3-4 tests
|
|
||||||
# Medium: 5-6 tests
|
|
||||||
# Hard: 7-10 tests
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Problems by Topic
|
### Step 6: VERIFY TESTS WORK (CRITICAL)
|
||||||
|
|
||||||
**Arrays/Lists**: frequency counting, deduplication, sliding window, two pointers
|
You MUST verify the tests are solvable before telling the user. Run this verification using inline Python - DO NOT write the solution to any file:
|
||||||
**Strings**: parsing, validation, transformation, pattern matching
|
|
||||||
**Hash Maps**: grouping, caching, lookup optimization
|
|
||||||
**Trees/Graphs**: traversal, path finding, hierarchy operations
|
|
||||||
**OOP Design**: class design, state management, encapsulation
|
|
||||||
**Data Processing**: ETL operations, aggregation, filtering pipelines
|
|
||||||
|
|
||||||
## After Generation
|
```bash
|
||||||
|
cd problems/{difficulty}/{problem-name} && python -c "
|
||||||
|
import sys
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
Tell the user:
|
# Define the actual solution INLINE (user cannot see this)
|
||||||
1. The path to their new problem: `problems/{difficulty}/{problem-name}/`
|
def {function_name}({params}):
|
||||||
2. How to start practicing: `uv run veetcode` then select the problem
|
# YOUR SOLUTION HERE - implement it fully
|
||||||
3. The file to edit: `solution.py`
|
...
|
||||||
|
|
||||||
Now, let's generate a problem! If difficulty or topic weren't provided, ask the user to choose.
|
# Create a fake 'solution' module
|
||||||
|
solution_module = ModuleType('solution')
|
||||||
|
solution_module.{function_name} = {function_name}
|
||||||
|
sys.modules['solution'] = solution_module
|
||||||
|
|
||||||
|
# Run pytest
|
||||||
|
import pytest
|
||||||
|
exit_code = pytest.main(['tests.py', '-v'])
|
||||||
|
sys.exit(exit_code)
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
If tests FAIL:
|
||||||
|
- Fix the tests (not the solution approach)
|
||||||
|
- Re-run verification
|
||||||
|
- Do NOT proceed until all tests pass
|
||||||
|
|
||||||
|
If tests PASS:
|
||||||
|
- Proceed to tell user the problem is ready
|
||||||
|
|
||||||
|
### Step 7: Confirm to User
|
||||||
|
|
||||||
|
Only after verification passes, tell the user:
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ Problem created and verified!
|
||||||
|
|
||||||
|
📁 Location: problems/{difficulty}/{problem-name}/
|
||||||
|
📝 Edit: solution.py
|
||||||
|
🚀 Run: uv run veetcode → select "{problem-name}"
|
||||||
|
|
||||||
|
Good luck!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Problem Style Guide
|
||||||
|
|
||||||
|
### DO:
|
||||||
|
- Real-world business context ("You're building a payment API...")
|
||||||
|
- Clear function signatures with type hints
|
||||||
|
- 2-3 concrete examples with explanations
|
||||||
|
- Explicit constraints
|
||||||
|
- Practical skills: data transformation, validation, business logic
|
||||||
|
|
||||||
|
### DON'T:
|
||||||
|
- Abstract algorithmic puzzles without context
|
||||||
|
- Mathematical framing ("Given an array of integers...")
|
||||||
|
- Textbook exercise style
|
||||||
|
- Overly complex for the difficulty level
|
||||||
|
|
||||||
|
## Difficulty Calibration
|
||||||
|
|
||||||
|
**Easy** (15-25 min):
|
||||||
|
- Single data structure (list, dict, set)
|
||||||
|
- 1-2 concepts
|
||||||
|
- 4-5 test cases
|
||||||
|
- ~20-30 line solution
|
||||||
|
|
||||||
|
**Medium** (30-40 min):
|
||||||
|
- Multiple data structures
|
||||||
|
- 3-4 concepts
|
||||||
|
- 6-8 test cases
|
||||||
|
- ~40-60 line solution
|
||||||
|
|
||||||
|
**Hard** (45-60 min):
|
||||||
|
- Custom classes or complex structures
|
||||||
|
- 5+ concepts
|
||||||
|
- 8-12 test cases
|
||||||
|
- ~80+ line solution
|
||||||
|
|
||||||
|
## Topic Ideas
|
||||||
|
|
||||||
|
- **Arrays**: frequency counting, deduplication, sliding window, two pointers
|
||||||
|
- **Strings**: parsing, validation, transformation, pattern matching
|
||||||
|
- **Hash Maps**: grouping, caching, lookup optimization, counting
|
||||||
|
- **Classes**: state management, encapsulation, business entities
|
||||||
|
- **Data Processing**: filtering, aggregation, transformation pipelines
|
||||||
|
|
||||||
|
Now, let's generate a problem! Ask for difficulty and topic if not provided.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
Reference examples for generating consistent, high-quality practice problems.
|
Reference examples for generating consistent, high-quality practice problems.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Easy Example: Email Validator
|
## Easy Example: Email Validator
|
||||||
|
|
||||||
**solution.py**:
|
**solution.py**:
|
||||||
|
|
@ -9,15 +11,14 @@ Reference examples for generating consistent, high-quality practice problems.
|
||||||
"""
|
"""
|
||||||
Email Validator
|
Email Validator
|
||||||
|
|
||||||
You're building a user registration system. Before storing emails in your
|
You're building a user registration system for an e-commerce platform.
|
||||||
database, you need to validate that they follow the correct format.
|
Before storing customer emails in your database, you need to validate
|
||||||
|
that they follow the correct format to prevent data quality issues.
|
||||||
Write a function that checks if an email address is valid.
|
|
||||||
|
|
||||||
Example 1:
|
Example 1:
|
||||||
Input: email = "user@example.com"
|
Input: email = "user@example.com"
|
||||||
Output: True
|
Output: True
|
||||||
Explanation: Has username, @, domain with dot
|
Explanation: Has username, single @, domain with dot
|
||||||
|
|
||||||
Example 2:
|
Example 2:
|
||||||
Input: email = "invalid-email"
|
Input: email = "invalid-email"
|
||||||
|
|
@ -31,8 +32,8 @@ Example 3:
|
||||||
|
|
||||||
Constraints:
|
Constraints:
|
||||||
- Input is always a string
|
- Input is always a string
|
||||||
- Valid emails have: non-empty username, exactly one @, domain with at least one dot
|
- Valid: non-empty username, exactly one @, domain with at least one dot
|
||||||
- No spaces allowed anywhere in the email
|
- No spaces allowed anywhere
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,32 +44,45 @@ def is_valid_email(email: str) -> bool:
|
||||||
|
|
||||||
**tests.py**:
|
**tests.py**:
|
||||||
```python
|
```python
|
||||||
|
"""Tests for email-validator."""
|
||||||
import pytest
|
import pytest
|
||||||
from solution import is_valid_email
|
from solution import is_valid_email
|
||||||
|
|
||||||
|
|
||||||
def test_valid_simple():
|
class TestBasicCases:
|
||||||
assert is_valid_email("user@example.com") == True
|
"""Test basic functionality with typical inputs."""
|
||||||
|
|
||||||
|
def test_valid_simple_email(self):
|
||||||
|
"""Test standard email format."""
|
||||||
|
assert is_valid_email("user@example.com") == True
|
||||||
|
|
||||||
|
def test_valid_with_subdomain(self):
|
||||||
|
"""Test email with subdomain."""
|
||||||
|
assert is_valid_email("user.name@mail.example.co.uk") == True
|
||||||
|
|
||||||
|
def test_invalid_missing_at(self):
|
||||||
|
"""Test email without @ symbol."""
|
||||||
|
assert is_valid_email("userexample.com") == False
|
||||||
|
|
||||||
|
|
||||||
def test_valid_with_dots():
|
class TestEdgeCases:
|
||||||
assert is_valid_email("user.name@example.co.uk") == True
|
"""Test edge cases and boundary conditions."""
|
||||||
|
|
||||||
|
def test_empty_string(self):
|
||||||
|
"""Test with empty input."""
|
||||||
|
assert is_valid_email("") == False
|
||||||
|
|
||||||
def test_invalid_no_at():
|
def test_empty_username(self):
|
||||||
assert is_valid_email("userexample.com") == False
|
"""Test with nothing before @."""
|
||||||
|
assert is_valid_email("@example.com") == False
|
||||||
|
|
||||||
|
def test_no_domain_dot(self):
|
||||||
|
"""Test domain without dot."""
|
||||||
|
assert is_valid_email("user@examplecom") == False
|
||||||
|
|
||||||
def test_invalid_no_domain_dot():
|
def test_spaces_in_email(self):
|
||||||
assert is_valid_email("user@examplecom") == False
|
"""Test email containing spaces."""
|
||||||
|
assert is_valid_email("user @example.com") == False
|
||||||
|
|
||||||
def test_invalid_empty_username():
|
|
||||||
assert is_valid_email("@example.com") == False
|
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_spaces():
|
|
||||||
assert is_valid_email("user @example.com") == False
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -80,11 +94,9 @@ def test_invalid_spaces():
|
||||||
"""
|
"""
|
||||||
Transaction Grouper
|
Transaction Grouper
|
||||||
|
|
||||||
You're building a financial dashboard. Users want to see their transactions
|
You're building a financial dashboard for a budgeting app. Users want
|
||||||
grouped by category, with totals calculated for each group.
|
to see their spending grouped by category with totals, so they can
|
||||||
|
understand where their money is going each month.
|
||||||
Given a list of transactions (each with amount, category, and date),
|
|
||||||
return a dictionary grouping transactions by category with their total.
|
|
||||||
|
|
||||||
Example 1:
|
Example 1:
|
||||||
Input: transactions = [
|
Input: transactions = [
|
||||||
|
|
@ -101,67 +113,85 @@ Example 2:
|
||||||
Explanation: No transactions means empty result
|
Explanation: No transactions means empty result
|
||||||
|
|
||||||
Constraints:
|
Constraints:
|
||||||
- Each transaction has "amount" (positive int), "category" (string), "date" (string)
|
- Each transaction has "amount" (positive int), "category" (str), "date" (str)
|
||||||
- Categories are case-sensitive
|
- Categories are case-sensitive
|
||||||
- Return categories in any order
|
- Return categories in any order
|
||||||
|
- Amount is always positive
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def group_transactions(transactions: list[dict]) -> dict[str, int]:
|
def group_transactions(transactions: list[dict]) -> dict[str, int]:
|
||||||
"""Return dictionary mapping category to total amount."""
|
"""Return dictionary mapping each category to its total amount."""
|
||||||
pass # Your implementation here
|
pass # Your implementation here
|
||||||
```
|
```
|
||||||
|
|
||||||
**tests.py**:
|
**tests.py**:
|
||||||
```python
|
```python
|
||||||
|
"""Tests for group-transactions."""
|
||||||
import pytest
|
import pytest
|
||||||
from solution import group_transactions
|
from solution import group_transactions
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_categories():
|
class TestBasicCases:
|
||||||
txns = [
|
"""Test basic functionality with typical inputs."""
|
||||||
{"amount": 50, "category": "food", "date": "2024-01-01"},
|
|
||||||
{"amount": 30, "category": "food", "date": "2024-01-02"},
|
def test_multiple_categories(self):
|
||||||
{"amount": 100, "category": "transport", "date": "2024-01-01"}
|
"""Test grouping across different categories."""
|
||||||
]
|
txns = [
|
||||||
assert group_transactions(txns) == {"food": 80, "transport": 100}
|
{"amount": 50, "category": "food", "date": "2024-01-01"},
|
||||||
|
{"amount": 30, "category": "food", "date": "2024-01-02"},
|
||||||
|
{"amount": 100, "category": "transport", "date": "2024-01-01"}
|
||||||
|
]
|
||||||
|
assert group_transactions(txns) == {"food": 80, "transport": 100}
|
||||||
|
|
||||||
|
def test_single_category(self):
|
||||||
|
"""Test all transactions in one category."""
|
||||||
|
txns = [
|
||||||
|
{"amount": 10, "category": "food", "date": "2024-01-01"},
|
||||||
|
{"amount": 20, "category": "food", "date": "2024-01-02"},
|
||||||
|
{"amount": 30, "category": "food", "date": "2024-01-03"}
|
||||||
|
]
|
||||||
|
assert group_transactions(txns) == {"food": 60}
|
||||||
|
|
||||||
|
def test_single_transaction(self):
|
||||||
|
"""Test with just one transaction."""
|
||||||
|
txns = [{"amount": 25, "category": "entertainment", "date": "2024-01-01"}]
|
||||||
|
assert group_transactions(txns) == {"entertainment": 25}
|
||||||
|
|
||||||
|
|
||||||
def test_empty_list():
|
class TestEdgeCases:
|
||||||
assert group_transactions([]) == {}
|
"""Test edge cases and boundary conditions."""
|
||||||
|
|
||||||
|
def test_empty_list(self):
|
||||||
|
"""Test with no transactions."""
|
||||||
|
assert group_transactions([]) == {}
|
||||||
|
|
||||||
def test_single_transaction():
|
def test_case_sensitive_categories(self):
|
||||||
txns = [{"amount": 25, "category": "entertainment", "date": "2024-01-01"}]
|
"""Test that categories are case-sensitive."""
|
||||||
assert group_transactions(txns) == {"entertainment": 25}
|
txns = [
|
||||||
|
{"amount": 10, "category": "Food", "date": "2024-01-01"},
|
||||||
|
{"amount": 20, "category": "food", "date": "2024-01-02"}
|
||||||
|
]
|
||||||
|
result = group_transactions(txns)
|
||||||
|
assert result == {"Food": 10, "food": 20}
|
||||||
|
|
||||||
|
def test_many_categories(self):
|
||||||
|
"""Test with many different categories."""
|
||||||
|
txns = [
|
||||||
|
{"amount": 1, "category": "a", "date": "2024-01-01"},
|
||||||
|
{"amount": 2, "category": "b", "date": "2024-01-01"},
|
||||||
|
{"amount": 3, "category": "c", "date": "2024-01-01"},
|
||||||
|
{"amount": 4, "category": "d", "date": "2024-01-01"}
|
||||||
|
]
|
||||||
|
assert group_transactions(txns) == {"a": 1, "b": 2, "c": 3, "d": 4}
|
||||||
|
|
||||||
def test_single_category_multiple_transactions():
|
def test_large_amounts(self):
|
||||||
txns = [
|
"""Test with large transaction amounts."""
|
||||||
{"amount": 10, "category": "food", "date": "2024-01-01"},
|
txns = [
|
||||||
{"amount": 20, "category": "food", "date": "2024-01-02"},
|
{"amount": 1000000, "category": "salary", "date": "2024-01-01"},
|
||||||
{"amount": 30, "category": "food", "date": "2024-01-03"}
|
{"amount": 500000, "category": "salary", "date": "2024-02-01"}
|
||||||
]
|
]
|
||||||
assert group_transactions(txns) == {"food": 60}
|
assert group_transactions(txns) == {"salary": 1500000}
|
||||||
|
|
||||||
|
|
||||||
def test_case_sensitive_categories():
|
|
||||||
txns = [
|
|
||||||
{"amount": 10, "category": "Food", "date": "2024-01-01"},
|
|
||||||
{"amount": 20, "category": "food", "date": "2024-01-02"}
|
|
||||||
]
|
|
||||||
result = group_transactions(txns)
|
|
||||||
assert result == {"Food": 10, "food": 20}
|
|
||||||
|
|
||||||
|
|
||||||
def test_many_categories():
|
|
||||||
txns = [
|
|
||||||
{"amount": 1, "category": "a", "date": "2024-01-01"},
|
|
||||||
{"amount": 2, "category": "b", "date": "2024-01-01"},
|
|
||||||
{"amount": 3, "category": "c", "date": "2024-01-01"},
|
|
||||||
{"amount": 4, "category": "d", "date": "2024-01-01"}
|
|
||||||
]
|
|
||||||
assert group_transactions(txns) == {"a": 1, "b": 2, "c": 3, "d": 4}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -173,164 +203,161 @@ def test_many_categories():
|
||||||
"""
|
"""
|
||||||
Rate Limiter
|
Rate Limiter
|
||||||
|
|
||||||
You're building an API gateway that needs to prevent abuse. Implement a
|
You're building an API gateway for a SaaS platform. To prevent abuse
|
||||||
rate limiter that tracks requests per user and enforces limits using
|
and ensure fair usage, you need to implement a rate limiter that tracks
|
||||||
a sliding window algorithm.
|
requests per user using a sliding window algorithm.
|
||||||
|
|
||||||
The rate limiter should allow at most `max_requests` per user within
|
The limiter should allow at most `max_requests` per user within any
|
||||||
any `window_seconds` time period.
|
`window_seconds` time period.
|
||||||
|
|
||||||
Example 1:
|
Example 1:
|
||||||
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
||||||
limiter.allow_request("user1", timestamp=0) # True (1st request)
|
limiter.allow_request("user1", timestamp=0) # True (1st request)
|
||||||
limiter.allow_request("user1", timestamp=30) # True (2nd request)
|
limiter.allow_request("user1", timestamp=30) # True (2nd request)
|
||||||
limiter.allow_request("user1", timestamp=45) # True (3rd request)
|
limiter.allow_request("user1", timestamp=45) # True (3rd request)
|
||||||
limiter.allow_request("user1", timestamp=50) # False (4th in 60s window)
|
limiter.allow_request("user1", timestamp=50) # False (limit reached)
|
||||||
limiter.allow_request("user1", timestamp=61) # True (1st request expired)
|
limiter.allow_request("user1", timestamp=61) # True (1st expired)
|
||||||
|
|
||||||
Example 2:
|
Example 2:
|
||||||
limiter = RateLimiter(max_requests=2, window_seconds=10)
|
limiter = RateLimiter(max_requests=2, window_seconds=10)
|
||||||
limiter.allow_request("user1", timestamp=0) # True
|
limiter.allow_request("user1", timestamp=0) # True
|
||||||
limiter.allow_request("user2", timestamp=0) # True (different user)
|
limiter.allow_request("user2", timestamp=0) # True (different user)
|
||||||
limiter.allow_request("user1", timestamp=5) # True
|
limiter.allow_request("user1", timestamp=5) # True
|
||||||
limiter.allow_request("user1", timestamp=8) # False (limit reached)
|
limiter.allow_request("user1", timestamp=8) # False
|
||||||
|
|
||||||
Constraints:
|
Constraints:
|
||||||
- max_requests >= 1
|
- max_requests >= 1
|
||||||
- window_seconds >= 1
|
- window_seconds >= 1
|
||||||
- timestamps are non-negative integers (seconds)
|
- Timestamps are non-negative integers (seconds)
|
||||||
- timestamps are always non-decreasing for a given user
|
- Timestamps are non-decreasing per user
|
||||||
- user_id is a non-empty string
|
- user_id is a non-empty string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RateLimiter:
|
class RateLimiter:
|
||||||
"""Sliding window rate limiter."""
|
"""Sliding window rate limiter for API request throttling."""
|
||||||
|
|
||||||
def __init__(self, max_requests: int, window_seconds: int):
|
def __init__(self, max_requests: int, window_seconds: int):
|
||||||
"""Initialize rate limiter with request limit and time window."""
|
"""Initialize with request limit and time window."""
|
||||||
pass # Your implementation here
|
pass # Your implementation here
|
||||||
|
|
||||||
def allow_request(self, user_id: str, timestamp: int) -> bool:
|
def allow_request(self, user_id: str, timestamp: int) -> bool:
|
||||||
"""Return True if request is allowed, False if rate limited."""
|
"""Return True if request allowed, False if rate limited."""
|
||||||
pass # Your implementation here
|
pass # Your implementation here
|
||||||
|
|
||||||
def get_remaining(self, user_id: str, timestamp: int) -> int:
|
def get_remaining(self, user_id: str, timestamp: int) -> int:
|
||||||
"""Return number of remaining requests allowed for user."""
|
"""Return remaining requests allowed for user at timestamp."""
|
||||||
pass # Your implementation here
|
pass # Your implementation here
|
||||||
```
|
```
|
||||||
|
|
||||||
**tests.py**:
|
**tests.py**:
|
||||||
```python
|
```python
|
||||||
|
"""Tests for rate-limiter."""
|
||||||
import pytest
|
import pytest
|
||||||
from solution import RateLimiter
|
from solution import RateLimiter
|
||||||
|
|
||||||
|
|
||||||
def test_basic_allow():
|
class TestBasicCases:
|
||||||
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
"""Test basic functionality with typical inputs."""
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 30) == True
|
def test_allow_within_limit(self):
|
||||||
assert limiter.allow_request("user1", 45) == True
|
"""Test requests within the limit are allowed."""
|
||||||
|
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 30) == True
|
||||||
|
assert limiter.allow_request("user1", 45) == True
|
||||||
|
|
||||||
|
def test_block_over_limit(self):
|
||||||
|
"""Test requests over limit are blocked."""
|
||||||
|
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 30) == True
|
||||||
|
assert limiter.allow_request("user1", 45) == False
|
||||||
|
|
||||||
|
def test_multiple_users_independent(self):
|
||||||
|
"""Test each user has independent limits."""
|
||||||
|
limiter = RateLimiter(max_requests=1, window_seconds=60)
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user2", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 30) == False
|
||||||
|
assert limiter.allow_request("user2", 30) == False
|
||||||
|
|
||||||
|
def test_get_remaining_basic(self):
|
||||||
|
"""Test remaining count decreases with requests."""
|
||||||
|
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
||||||
|
assert limiter.get_remaining("user1", 0) == 3
|
||||||
|
limiter.allow_request("user1", 0)
|
||||||
|
assert limiter.get_remaining("user1", 0) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_rate_limit_exceeded():
|
class TestEdgeCases:
|
||||||
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
"""Test edge cases and boundary conditions."""
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 30) == True
|
|
||||||
assert limiter.allow_request("user1", 45) == False
|
|
||||||
|
|
||||||
|
def test_window_expiration(self):
|
||||||
|
"""Test old requests expire from window."""
|
||||||
|
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 30) == True
|
||||||
|
assert limiter.allow_request("user1", 45) == False
|
||||||
|
assert limiter.allow_request("user1", 61) == True
|
||||||
|
|
||||||
def test_window_expiration():
|
def test_single_request_limit(self):
|
||||||
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
"""Test with limit of 1 request."""
|
||||||
assert limiter.allow_request("user1", 0) == True
|
limiter = RateLimiter(max_requests=1, window_seconds=10)
|
||||||
assert limiter.allow_request("user1", 30) == True
|
assert limiter.allow_request("user1", 0) == True
|
||||||
assert limiter.allow_request("user1", 45) == False
|
assert limiter.allow_request("user1", 5) == False
|
||||||
assert limiter.allow_request("user1", 61) == True # First request expired
|
assert limiter.allow_request("user1", 11) == True
|
||||||
|
|
||||||
|
def test_new_user_full_allowance(self):
|
||||||
|
"""Test new users start with full allowance."""
|
||||||
|
limiter = RateLimiter(max_requests=5, window_seconds=60)
|
||||||
|
limiter.allow_request("user1", 0)
|
||||||
|
assert limiter.get_remaining("new_user", 20) == 5
|
||||||
|
|
||||||
def test_multiple_users_independent():
|
def test_remaining_after_expiration(self):
|
||||||
limiter = RateLimiter(max_requests=1, window_seconds=60)
|
"""Test remaining increases as requests expire."""
|
||||||
assert limiter.allow_request("user1", 0) == True
|
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
||||||
assert limiter.allow_request("user2", 0) == True
|
limiter.allow_request("user1", 0)
|
||||||
assert limiter.allow_request("user1", 30) == False
|
limiter.allow_request("user1", 30)
|
||||||
assert limiter.allow_request("user2", 30) == False
|
assert limiter.get_remaining("user1", 30) == 0
|
||||||
|
assert limiter.get_remaining("user1", 61) == 1
|
||||||
|
|
||||||
|
def test_rapid_same_timestamp(self):
|
||||||
|
"""Test multiple requests at same timestamp."""
|
||||||
|
limiter = RateLimiter(max_requests=3, window_seconds=1)
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 0) == True
|
||||||
|
assert limiter.allow_request("user1", 0) == False
|
||||||
|
|
||||||
def test_get_remaining():
|
def test_exact_window_boundary(self):
|
||||||
limiter = RateLimiter(max_requests=3, window_seconds=60)
|
"""Test behavior at exact window boundary."""
|
||||||
assert limiter.get_remaining("user1", 0) == 3
|
limiter = RateLimiter(max_requests=1, window_seconds=10)
|
||||||
limiter.allow_request("user1", 0)
|
assert limiter.allow_request("user1", 0) == True
|
||||||
assert limiter.get_remaining("user1", 0) == 2
|
assert limiter.allow_request("user1", 10) == False
|
||||||
limiter.allow_request("user1", 30)
|
assert limiter.allow_request("user1", 11) == True
|
||||||
assert limiter.get_remaining("user1", 30) == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_remaining_after_expiration():
|
|
||||||
limiter = RateLimiter(max_requests=2, window_seconds=60)
|
|
||||||
limiter.allow_request("user1", 0)
|
|
||||||
limiter.allow_request("user1", 30)
|
|
||||||
assert limiter.get_remaining("user1", 30) == 0
|
|
||||||
assert limiter.get_remaining("user1", 61) == 1 # First expired
|
|
||||||
|
|
||||||
|
|
||||||
def test_single_request_limit():
|
|
||||||
limiter = RateLimiter(max_requests=1, window_seconds=10)
|
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 5) == False
|
|
||||||
assert limiter.allow_request("user1", 10) == False
|
|
||||||
assert limiter.allow_request("user1", 11) == True
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_user_has_full_allowance():
|
|
||||||
limiter = RateLimiter(max_requests=5, window_seconds=60)
|
|
||||||
limiter.allow_request("user1", 0)
|
|
||||||
limiter.allow_request("user1", 10)
|
|
||||||
assert limiter.get_remaining("new_user", 20) == 5
|
|
||||||
|
|
||||||
|
|
||||||
def test_rapid_requests():
|
|
||||||
limiter = RateLimiter(max_requests=3, window_seconds=1)
|
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 0) == True
|
|
||||||
assert limiter.allow_request("user1", 0) == False
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Topic Ideas by Concept
|
## Topic Quick Reference
|
||||||
|
|
||||||
### Arrays/Lists
|
### Arrays/Lists
|
||||||
- Remove duplicates preserving order
|
- Frequency counting, deduplication, sliding window
|
||||||
- Find pairs that sum to target
|
- Two pointers, rotation, merging sorted arrays
|
||||||
- Merge sorted arrays
|
|
||||||
- Rotate array by k positions
|
|
||||||
- Find missing number in sequence
|
|
||||||
|
|
||||||
### Strings
|
### Strings
|
||||||
- Validate email/URL/phone format
|
- Validation (email, URL, phone), parsing CSV/JSON
|
||||||
- Count word frequency
|
- Pattern matching, compression, transformation
|
||||||
- Find longest palindromic substring
|
|
||||||
- Parse CSV line with quotes
|
|
||||||
- Compress string (aaabbc -> a3b2c1)
|
|
||||||
|
|
||||||
### Hash Maps
|
### Hash Maps
|
||||||
- Group items by property
|
- Grouping by property, counting occurrences
|
||||||
- Find first non-repeating character
|
- Two sum variants, caching, anagram detection
|
||||||
- Two sum / three sum variations
|
|
||||||
- LRU Cache implementation
|
|
||||||
- Anagram grouping
|
|
||||||
|
|
||||||
### Classes/OOP
|
### Classes/OOP
|
||||||
- Shopping cart with discounts
|
- Shopping cart, bank account, task scheduler
|
||||||
- Bank account with transaction history
|
- State machines, event systems, entity modeling
|
||||||
- Task scheduler with priorities
|
|
||||||
- Event emitter / pub-sub
|
|
||||||
- State machine implementation
|
|
||||||
|
|
||||||
### Data Processing
|
### Data Processing
|
||||||
- Filter and transform records
|
- Filter/map/reduce pipelines, aggregation
|
||||||
- Aggregate statistics
|
- Interval merging, pagination, deduplication
|
||||||
- Merge overlapping intervals
|
|
||||||
- Topological sort of dependencies
|
|
||||||
- Pagination with cursor
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue