Skip to content
Open
219 changes: 219 additions & 0 deletions examples/cairo/scripts/contains_duplicate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Contains Duplicate - Cairo Implementation

A complete implementation of the classic "Contains Duplicate" problem in Cairo v1, featuring two different algorithmic approaches with comprehensive testing and documentation.

## Problem Statement

Given an array of integers, return `true` if any value appears at least twice in the array, and `false` if every element is distinct.

## Examples

```cairo
// Example 1: Contains duplicate
let arr = array![1, 2, 3, 1];
assert!(contains_duplicate_naive(arr.span())); // true

// Example 2: No duplicates
let arr = array![1, 2, 3, 4];
assert!(!contains_duplicate_optimized(arr.span())); // false

// Example 3: Multiple duplicates
let arr = array![1, 1, 1, 3, 3, 4, 3, 2, 4, 2];
assert!(contains_duplicate_optimized(arr.span())); // true
```

## Algorithmic Approaches

This implementation provides two distinct approaches, each with different time/space tradeoffs:

### 1. Naive Approach (`contains_duplicate_naive`)
- **Algorithm**: Nested loops comparing every pair of elements
- **Time Complexity**: O(nΒ²) - worst case compares all pairs
- **Space Complexity**: O(1) - no additional storage needed
- **Best for**: Small arrays where simplicity is preferred over performance

```cairo
pub fn contains_duplicate_naive(nums: Span<felt252>) -> bool
```

**How it works:**
1. For each element at position `i`
2. Check all subsequent elements at positions `j > i`
3. Return `true` immediately when a duplicate is found (early exit)
4. Return `false` only after checking all pairs

### 2. Optimized Approach (`contains_duplicate_optimized`)
- **Algorithm**: Single pass using Dict as hash set
- **Time Complexity**: O(n) - single iteration with O(1) dict operations
- **Space Complexity**: O(n) - worst case stores all unique elements
- **Best for**: Large arrays where performance is critical

```cairo
pub fn contains_duplicate_optimized(nums: Span<felt252>) -> bool
```

**How it works:**
1. Iterate through array once
2. For each element, check if it exists in our "seen" set (Dict)
3. If exists β†’ duplicate found, return `true`
4. If not exists β†’ add to "seen" set, continue
5. If loop completes β†’ no duplicates, return `false`

### Alternative Optimized Implementation
Also included: `contains_duplicate_optimized_alt` using explicit status tracking in Dict for educational purposes.

## Complexity Analysis

| Approach | Time Complexity | Space Complexity | Best Case | Worst Case | Average Case |
|----------|----------------|------------------|-----------|------------|--------------|
| **Naive** | O(nΒ²) | O(1) | O(1) - duplicate at start | O(nΒ²) - no duplicates | O(nΒ²) |
| **Optimized** | O(n) | O(n) | O(1) - duplicate at start | O(n) - no duplicates | O(n) |

### Performance Considerations

- **For small arrays (n < 50)**: Both approaches perform similarly
- **For medium arrays (50 ≀ n ≀ 1000)**: Optimized approach starts showing benefits
- **For large arrays (n > 1000)**: Optimized approach significantly outperforms naive

## Cairo-Specific Implementation Notes

### Working with `felt252`
- All values are `felt252` (Cairo's native field element type)
- Supports the full range of field elements (0 to p-1 where p β‰ˆ 2²⁡¹)
- All values are positive in felt252 representation

### Memory Efficiency with `Span<T>`
- Uses `Span<felt252>` for read-only array access
- No copying of input data β†’ memory efficient
- Safe indexing with bounds checking

### Dict as Hash Set
- `Felt252Dict<felt252>` acts as a hash set with status tracking
- Uses 0 for "not seen" and 1 for "seen" status
- Average O(1) insertion and lookup operations
- Built-in Cairo type with efficient implementation

### Early Exit Optimization
Both approaches implement early exit patterns:
- Return immediately when duplicate found
- Avoid unnecessary computation
- Particularly effective for arrays with early duplicates

## Project Structure

```
contains_duplicate/
β”œβ”€β”€ Scarb.toml # Project configuration
β”œβ”€β”€ src/
β”‚ β”œβ”€β”€ lib.cairo # Main library with module declarations
β”‚ β”œβ”€β”€ contains_duplicate_naive.cairo # O(nΒ²) implementation
β”‚ └── contains_duplicate_optimized.cairo # O(n) implementation
β”œβ”€β”€ tests/
β”‚ └── test_contains_duplicate.cairo # Comprehensive test suite
└── README.md # This documentation
```

## Building and Testing

### Prerequisites
- Cairo v1 (2024_07 edition)
- Scarb build tool
- Cairo Test framework

### Commands

```bash
# Navigate to project directory
cd examples/cairo/scripts/contains_duplicate

# Build the project
scarb build

# Run all tests
scarb test

# Run specific test pattern
scarb test test_example

# Run with verbose output
scarb test -v
```

## Test Coverage

The test suite includes:

- **Base Cases**: Empty arrays, single elements, two elements
- **Required Examples**: All examples from problem specification
- **Edge Cases**: Large values, duplicates at different positions
- **Stress Tests**: Long arrays testing performance characteristics
- **Boundary Cases**: Special Cairo felt252 value handling

All tests verify consistency between both approaches to ensure correctness.

## When to Use Which Approach

### Choose Naive Approach When:
- Array size is small (< 50 elements)
- Memory usage is extremely constrained
- Code simplicity is more important than performance
- Educational purposes (easier to understand)

### Choose Optimized Approach When:
- Array size is medium to large (β‰₯ 50 elements)
- Performance is critical
- Expected to process many arrays
- Memory usage is not a primary constraint

## Educational Value

This implementation demonstrates several important concepts:

1. **Algorithm Design**: Trade-offs between time and space complexity
2. **Cairo Programming**: Proper use of `felt252`, `Span<T>`, and `Felt252Dict`
3. **Testing Strategy**: Comprehensive test coverage with edge cases
4. **Documentation**: Clear complexity analysis and usage guidelines
5. **Code Organization**: Clean module structure and exports

## Function Signatures

```cairo
// Naive O(nΒ²) approach
pub fn contains_duplicate_naive(nums: Span<felt252>) -> bool

// Optimized O(n) approach
pub fn contains_duplicate_optimized(nums: Span<felt252>) -> bool

// Alternative optimized approach (educational)
pub fn contains_duplicate_optimized_alt(nums: Span<felt252>) -> bool
```

## Usage Example

```cairo
use contains_duplicate::contains_duplicate_naive::contains_duplicate_naive;
use contains_duplicate::contains_duplicate_optimized::contains_duplicate_optimized;

fn main() {
let arr = array![1, 2, 3, 1];
let span = arr.span();

// Both approaches return the same result
assert!(contains_duplicate_naive(span));
assert!(contains_duplicate_optimized(span));
}
```

## Contributing

When extending this implementation:

1. Maintain the existing function signatures
2. Add comprehensive tests for new functionality
3. Update complexity analysis in documentation
4. Follow Cairo naming and style conventions
5. Include clear comments explaining algorithmic choices

## License

This implementation is part of the Starknet Cairo learning resources and demonstrates best practices for Cairo v1 development.
6 changes: 6 additions & 0 deletions examples/cairo/scripts/contains_duplicate/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "contains_duplicate"
version = "0.1.0"
11 changes: 11 additions & 0 deletions examples/cairo/scripts/contains_duplicate/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "contains_duplicate"
version = "0.1.0"
edition = "2024_07"

# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html

[dependencies]

[dev-dependencies]
cairo_test = "2.9.2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/// Naive approach to check for duplicates using nested loops
/// Time Complexity: O(nΒ²) - we compare every element with every other element
/// Space Complexity: O(1) - no additional storage needed besides input
///
/// This implementation uses the straightforward brute-force approach:
/// - For each element, check all subsequent elements
/// - Return true immediately when a duplicate is found (early exit)
/// - Return false only after checking all pairs
///
/// In Cairo, we work with Span<felt252> for read-only array views,
/// which is memory-efficient and safe for large datasets.

/// Check if array contains any duplicate values using naive O(nΒ²) approach
///
/// # Arguments
/// * `nums` - Span of felt252 values to check for duplicates
///
/// # Returns
/// * `true` if any value appears at least twice
/// * `false` if all elements are distinct
///
/// # Examples
/// ```
/// let arr = array![1, 2, 3, 1];
/// assert!(contains_duplicate_naive(arr.span()));
///
/// let arr = array![1, 2, 3, 4];
/// assert!(!contains_duplicate_naive(arr.span()));
/// ```
pub fn contains_duplicate_naive(nums: Span<felt252>) -> bool {
let len = nums.len();

// Empty arrays and single elements cannot contain duplicates
if len <= 1 {
return false;
}

// Compare each element with every other element after it
// Using nested loops: outer loop for current element, inner for comparison
let mut i: usize = 0;
while i < len {
let current = *nums.at(i);

// Check current element against all subsequent elements
let mut j: usize = i + 1;
while j < len {
let other = *nums.at(j);

// Early exit: duplicate found
if current == other {
return true;
}

j += 1;
};

i += 1;
};

// No duplicates found after checking all pairs
false
}
Loading