fliggertiggybet 1.0.0
A Python library for parsing and generating Fliggertiggybet numerals - an emoji-based numeral system
Requires Python: >=3.10
Keywords: emoji, fliggertiggybet, number-conversion, numerals, roman-numerals

# Fliggertiggybet 🎆

[![CI](https://github.com/sinmentis/Fliggertiggybet/actions/workflows/ci.yml/badge.svg)](https://github.com/sinmentis/Fliggertiggybet/actions/workflows/ci.yml)
[![Python Version](https://img.shields.io/pypi/pyversions/fliggertiggybet.svg)](https://pypi.org/project/fliggertiggybet/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Python library for parsing and generating **Fliggertiggybet numerals** — an emoji-based numeral system similar to Roman numerals.

## 📖 Background

Fliggertiggybet numerals are the number system used on the fictional planet Fliggertiggybet. They use emojis instead of letters and combine additive and subtractive notation to represent numbers from 1 to 3999.

### Symbol Values

| Symbol | Value | Meaning |
|--------|-------|---------|
| ☝️ | 1 | One finger |
| ✌️ | 2 | Two fingers (peace sign) |
| 👌 | 5 | Five fingers (OK sign) |
| ✋ | 10 | Open hand |
| 🙌 | 50 | Both hands raised |
| 💯 | 100 | Hundred points |
| 🎊 | 500 | Celebration |
| 🎆 | 1000 | Fireworks |

### Notation Rules

1. **Additive Notation**: Symbols are written largest to smallest, left to right, and their values are added.
   - Example: `🙌✋👌` = 50 + 10 + 5 = **65**

2. **Subtractive Notation**: When a smaller value precedes a larger value, subtract the smaller from the larger.
   - Example: `☝️✋` = 10 - 1 = **9**
   - Example: `✋✋💯` = 100 - 10 - 10 = **80**
   - Valid subtractive pairs:
     - ☝️ can precede ✋ (10) or 👌 (5)
     - ✌️ can precede ✋ (10) or 👌 (5)
     - ✋ can precede 🙌 (50) or 💯 (100)
     - 💯 can precede 🎊 (500) or 🎆 (1000)

3. **Repetition Limits**:
   - ☝️, ✌️, 👌, 🙌, 🎊 — maximum 1 consecutive
   - ✋, 💯 — maximum 2 consecutive
   - 🎆 — maximum 3 consecutive

4. **Valid Range**: 1 to 3999

## 🚀 Installation

### From PyPI (when published)

```bash
pip install fliggertiggybet
```

### From Source

```bash
git clone https://github.com/slyu/Fliggertiggybet.git
cd Fliggertiggybet
pip install -e .
```

### Development Installation

```bash
pip install -e ".[dev]"
pre-commit install
```

## 💻 Usage

### Command Line Interface

```bash
# Convert integer to Fliggertiggybet numeral
$ fliggertiggybet 42
42
✋🙌✌️

# Convert Fliggertiggybet numeral to integer
$ fliggertiggybet ☝️✋
9
☝️✋

# Invalid input handling
$ fliggertiggybet 🙌🙌🔥💯💯✋🐤✋🐍☝️
Invalid Fliggertiggybet numeral

# Strict mode (enforce all rules)
$ fliggertiggybet --strict ✋✋✋
Error: Symbol ✋ repeated 3 times (max allowed: 2)
```

### Python Library

```python
from fliggertiggybet import FliggertNumber, to_integer, to_fliggertiggybet

# Convert integer to Fliggertiggybet numeral
numeral = to_fliggertiggybet(42)
print(numeral)  # ✋🙌✌️

# Convert Fliggertiggybet numeral to integer
value = to_integer("☝️✋")
print(value)  # 9

# Use the FliggertNumber class for an abstract data type
n = FliggertNumber(1987)
print(n.numeral)  # 🎆💯🎆✋✋💯👌✌️
print(n.value)    # 1987
print(int(n))     # 1987
print(str(n))     # 🎆💯🎆✋✋💯👌✌️

# Parse from numeral string
n = FliggertNumber.from_numeral("🙌✋👌")
print(n.value)    # 65

# Safe parsing (returns None on failure)
n = FliggertNumber.try_parse("invalid🔥")
print(n)  # None

# Arithmetic operations
a = FliggertNumber(10)
b = FliggertNumber(5)
result = a + b
print(result.value)  # 15

# Comparison operations
print(a > b)  # True
print(a == 10)  # True

# Iteration over a range
for n in FliggertNumber.range(1, 5):
    print(f"{n.value}: {n.numeral}")
# 1: ☝️
# 2: ✌️
# 3: ✌️👌
# 4: ☝️👌
```

### Strict vs Lenient Mode

Following [Postel's Law](https://en.wikipedia.org/wiki/Robustness_principle), the library is **liberal in what it accepts** (lenient mode by default) but **conservative in what it outputs** (always canonical form).

```python
# Lenient mode (default): accepts non-standard input
value = to_integer("✋✋✋✋✌️")  # Violates repetition rules
print(value)  # 42 (parsed anyway, with warning logged)

# Strict mode: enforces all rules
from fliggertiggybet import FliggertValidationError

try:
    to_integer("✋✋✋", strict=True)
except FliggertValidationError as e:
    print(e)  # Symbol ✋ repeated 3 times (max allowed: 2)

# Output is always canonical
n = FliggertNumber.from_numeral("✋✋✋✋✌️")
print(n.numeral)  # ✋🙌✌️ (canonical form for 42)
```

## 🧪 Testing

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=fliggertiggybet --cov-report=html

# Run specific test file
pytest tests/test_to_integer.py -v
```

## 🔧 Development

### Static Analysis

```bash
# Lint code
ruff check .

# Format code
ruff format .

# Type checking
mypy src/fliggertiggybet

# Run all checks via pre-commit
pre-commit run --all-files
```

### Project Structure

```
Fliggertiggybet/
├── src/
│   └── fliggertiggybet/
│       ├── __init__.py      # Package exports
│       ├── cli.py           # Command-line interface
│       ├── core.py          # Main conversion logic
│       ├── exceptions.py    # Custom exceptions
│       ├── types.py         # Data types and symbols
│       └── py.typed         # PEP 561 marker
├── tests/
│   ├── conftest.py          # Pytest fixtures
│   ├── test_cli.py          # CLI tests
│   ├── test_to_integer.py   # Parsing tests
│   ├── test_to_fliggertiggybet.py  # Generation tests
│   └── test_validation.py   # Validation tests
├── .github/
│   └── workflows/
│       ├── ci.yml           # CI pipeline
│       └── release.yml      # Release automation
├── pyproject.toml           # Project configuration
├── test_cases.json          # Test case definitions
├── instruction.md           # Original problem statement
└── README.md
```

## 📚 API Reference

### Functions

| Function | Description |
|----------|-------------|
| `to_integer(numeral, *, strict=False)` | Convert a Fliggertiggybet numeral string to an integer |
| `to_fliggertiggybet(number, *, strict=False)` | Convert an integer to a Fliggertiggybet numeral string |

### Classes

| Class | Description |
|-------|-------------|
| `FliggertNumber` | Abstract data type representing a Fliggertiggybet numeral |
| `Symbol` | Data class representing a single Fliggertiggybet symbol |

### Exceptions

| Exception | Description |
|-----------|-------------|
| `FliggertError` | Base exception for all Fliggertiggybet errors |
| `FliggertParseError` | Raised when a numeral string cannot be parsed |
| `FliggertValidationError` | Raised when validation rules are violated (strict mode) |
| `FliggertRangeError` | Raised when a value is outside the valid range (1-3999) |

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 References

- [Roman Numeral Conversion Algorithms](https://en.wikipedia.org/wiki/Roman_numerals#Reading_Roman_numerals)
- [Postel's Law (Robustness Principle)](https://en.wikipedia.org/wiki/Robustness_principle)
- [Python Packaging User Guide](https://packaging.python.org/)
- [PEP 561 – Distributing and Packaging Type Information](https://peps.python.org/pep-0561/)

---

May the Fliggertians smile upon your code! 🎆✨