mingot 0.6.0

Leptos UI library for applications demanding mathematical precision - u64+ integers, arbitrary-precision decimals, zero precision loss
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# High-Precision Number Input Support

## Context

Industrial Algebra's product ecosystem heavily relies on Amari (u64+ precision mathematical computing library with geometric algebra, tropical algebra, and automatic differentiation). Mingot should provide first-class support for high-precision number inputs rather than relying on HTML5's limited number input capabilities.

## Problem Statement

### HTML5 Number Input Limitations

1. **JavaScript Number Precision**: HTML5 `<input type="number">` is constrained by JavaScript's 64-bit floating-point representation:
   - Maximum safe integer: `2^53 - 1` (9,007,199,254,740,991)
   - Precision loss beyond ~15-17 significant digits
   - Cannot represent u64, u128, or arbitrary-precision numbers accurately

2. **Scientific Applications**: Industrial Algebra products require:
   - u64+ precision for mathematical computations
   - Exact decimal representation (no floating-point errors)
   - Integration with Amari's geometric algebra types
   - Support for very large and very small numbers

3. **Current Workaround**: Using `<input type="text">` with manual validation loses:
   - Semantic meaning
   - Browser accessibility features for numeric input
   - Consistent UX patterns

## Proposed Solution

### New Component: `NumberInput`

A specialized component for high-precision number input that:
- Uses text-based input for unlimited precision
- Validates against configurable precision requirements
- Integrates with Amari types
- Provides formatting and display options
- Maintains accessibility

### Component API Design

```rust
use amari::Number; // or appropriate Amari type

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NumberInputPrecision {
    U64,           // Unsigned 64-bit
    U128,          // Unsigned 128-bit
    I64,           // Signed 64-bit
    I128,          // Signed 128-bit
    Decimal(u32),  // Fixed decimal places
    Arbitrary,     // Arbitrary precision (uses Amari's Number type)
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum NumberInputFormat {
    Standard,      // 123456789
    Thousand,      // 123,456,789
    Scientific,    // 1.23e8
    Engineering,   // 123.4e6
}

#[component]
pub fn NumberInput(
    // Core value handling
    #[prop(optional)] value: Option<RwSignal<String>>,
    #[prop(optional)] on_change: Option<Callback<String>>,
    #[prop(optional)] on_valid_change: Option<Callback<Result<Number, ParseError>>>,

    // Precision configuration
    #[prop(optional)] precision: Option<NumberInputPrecision>,
    #[prop(optional, into)] min: Option<String>,
    #[prop(optional, into)] max: Option<String>,

    // Display formatting
    #[prop(optional)] format: Option<NumberInputFormat>,
    #[prop(optional)] decimal_separator: Option<char>, // Default: '.'
    #[prop(optional)] thousand_separator: Option<char>, // Default: ','

    // Validation
    #[prop(optional)] allow_negative: bool,
    #[prop(optional)] allow_decimal: bool,
    #[prop(optional)] allow_scientific: bool,

    // Standard form input props
    #[prop(optional)] variant: Option<InputVariant>,
    #[prop(optional)] size: Option<InputSize>,
    #[prop(optional, into)] placeholder: Option<String>,
    #[prop(optional, into)] disabled: Signal<bool>,
    #[prop(optional, into)] error: Option<String>,
    #[prop(optional)] required: bool,
    #[prop(optional, into)] label: Option<String>,
    #[prop(optional, into)] description: Option<String>,
    #[prop(optional, into)] class: Option<String>,
    #[prop(optional, into)] style: Option<String>,
) -> impl IntoView
```

### Usage Examples

#### Example 1: U64 Integer Input
```rust
use mingot::prelude::*;

let value = RwSignal::new(String::from("9223372036854775807"));

view! {
    <NumberInput
        value=value
        precision=NumberInputPrecision::U64
        format=NumberInputFormat::Thousand
        label="Large Integer Value"
        description="Enter a 64-bit unsigned integer"
    />
}
```

#### Example 2: High-Precision Decimal
```rust
let volatility = RwSignal::new(String::from("0.00000123456789"));

view! {
    <NumberInput
        value=volatility
        precision=NumberInputPrecision::Decimal(14)
        allow_decimal=true
        min="0"
        max="1"
        label="Volatility"
        description="14 decimal places of precision"
    />
}
```

#### Example 3: Amari Integration
```rust
use amari::Number;

let (amari_value, set_amari_value) = create_signal(None::<Number>);

view! {
    <NumberInput
        precision=NumberInputPrecision::Arbitrary
        on_valid_change=Callback::new(move |result: Result<Number, ParseError>| {
            if let Ok(num) = result {
                set_amari_value.set(Some(num));
            }
        })
        label="Arbitrary Precision Value"
        description="Powered by Amari"
    />
}
```

#### Example 4: Scientific Notation
```rust
view! {
    <NumberInput
        precision=NumberInputPrecision::Arbitrary
        format=NumberInputFormat::Scientific
        allow_scientific=true
        allow_negative=true
        label="Scientific Value"
        placeholder="1.23e-8"
    />
}
```

## Implementation Strategy

### Phase 1: Foundation (Without Amari)
Build core NumberInput component with:
- Text-based input validation
- Support for u64, u128, i64, i128 via Rust's standard library
- Formatting options (thousand separators, etc.)
- Range validation
- Accessibility features

**Dependencies**: None new (use Rust stdlib)

### Phase 2: Amari Integration
Add Amari as optional dependency:
- `NumberInputPrecision::Arbitrary` mode
- Integration with Amari's `Number` type
- Callbacks that return parsed Amari types
- Validation using Amari's precision capabilities

**Dependencies**:
```toml
[dependencies]
amari = { version = "0.9.10", optional = true }

[features]
default = ["csr"]
high-precision = ["amari"]
```

### Phase 3: Advanced Features
- Auto-formatting on blur
- Increment/decrement controls for high-precision values
- Copy/paste handling with format detection
- Keyboard shortcuts (scientific notation toggle, etc.)
- Integration with Mingot's validation framework

## Technical Considerations

### 1. String-Based Value Storage

```rust
// Store as string to preserve exact input
let raw_value = RwSignal::new(String::new());

// Parse on demand
let parsed_value = move || {
    match precision {
        NumberInputPrecision::U64 => raw_value.get().parse::<u64>(),
        NumberInputPrecision::Arbitrary => {
            #[cfg(feature = "high-precision")]
            amari::Number::from_str(&raw_value.get())
        }
        // ...
    }
};
```

### 2. Real-Time Validation

```rust
let validate_input = move |input: String| -> ValidationResult {
    match precision {
        NumberInputPrecision::U64 => {
            input.parse::<u64>()
                .map_err(|e| ValidationError::new("Invalid u64"))
        }
        NumberInputPrecision::Decimal(places) => {
            validate_decimal_places(&input, places)
        }
        // ...
    }
};
```

### 3. Formatting Display

```rust
let format_display = move |value: &str| -> String {
    match format {
        NumberInputFormat::Thousand => {
            add_thousand_separators(value, thousand_separator.unwrap_or(','))
        }
        NumberInputFormat::Scientific => {
            convert_to_scientific(value)
        }
        // ...
    }
};
```

### 4. Input Filtering

```rust
// Only allow valid characters based on configuration
let filter_input = move |char: char| -> bool {
    match char {
        '0'..='9' => true,
        '-' => allow_negative && /* is first char */,
        '.' => allow_decimal && /* not already present */,
        'e' | 'E' => allow_scientific && /* valid position */,
        _ if thousand_separator == Some(char) => format == NumberInputFormat::Thousand,
        _ => false,
    }
};
```

## Accessibility Considerations

1. **ARIA Attributes**:
   ```rust
   <input
       type="text"
       inputmode="decimal"  // Mobile keyboard optimization
       role="spinbutton"    // Semantic role for screen readers
       aria-valuemin=min
       aria-valuemax=max
       aria-valuenow=current_value
       aria-invalid=is_error
   />
   ```

2. **Keyboard Navigation**:
   - Arrow up/down: Increment/decrement by step size
   - Shift + Arrow: Increment/decrement by 10x step
   - Ctrl + Arrow: Increment/decrement by 100x step

3. **Screen Reader Support**:
   - Announce validation errors
   - Announce value changes
   - Describe precision and range constraints

## Testing Strategy

### Unit Tests
```rust
#[cfg(test)]
mod tests {
    #[test]
    fn test_u64_validation() {
        assert!(validate_u64("18446744073709551615").is_ok());
        assert!(validate_u64("18446744073709551616").is_err()); // overflow
    }

    #[test]
    fn test_decimal_precision() {
        assert!(validate_decimal("0.123456", 6).is_ok());
        assert!(validate_decimal("0.1234567", 6).is_err()); // too many decimals
    }

    #[test]
    fn test_thousand_separator_formatting() {
        assert_eq!(format_thousands("1234567"), "1,234,567");
    }

    #[cfg(feature = "high-precision")]
    #[test]
    fn test_amari_integration() {
        let num = parse_to_amari("123.456789012345678901234567890");
        assert!(num.is_ok());
    }
}
```

### Integration Tests
- Test with real Amari types in actual applications
- Verify WASM compatibility
- Performance testing with very large numbers
- Edge case testing (overflow, underflow, precision limits)

## Migration Path

### Existing Input Usage
```rust
// Before: Limited to JavaScript Number precision
<Input input_type="number" step="0.01" label="Value" />
```

### After: High-Precision Support
```rust
// After: Unlimited precision
<NumberInput
    precision=NumberInputPrecision::Decimal(14)
    allow_decimal=true
    label="Value"
/>
```

### Compatibility
- Keep existing `Input` component for standard use cases
- `NumberInput` is opt-in for high-precision requirements
- Gradual migration path - components coexist

## Open Questions

1. **Amari Type Selection**: Which Amari types should we support?
   - `amari::Number`?
   - Geometric algebra types?
   - Tropical algebra types?

2. **Feature Flag Strategy**:
   - Should Amari integration be optional via feature flag?
   - Or should we make Amari a required dependency for Mingot?

3. **Performance**:
   - String parsing overhead vs native number types
   - WASM binary size impact
   - Real-time validation performance for very large numbers

4. **Browser Compatibility**:
   - Clipboard API for copy/paste
   - IME (Input Method Editor) handling
   - Mobile keyboard optimization

5. **Validation Framework Integration**:
   - Should NumberInput integrate with Mingot's existing validation system?
   - Custom validators for domain-specific number constraints?

## Next Steps

1. **Gather Requirements**: Survey Industrial Algebra products for specific precision needs
2. **Prototype Phase 1**: Build basic NumberInput without Amari integration
3. **Validate Approach**: Test with real-world use cases from Ultramarine-Red or other products
4. **Phase 2 Implementation**: Add Amari integration after Phase 1 validation
5. **Documentation**: Comprehensive examples and migration guide
6. **Performance Testing**: Benchmark against requirements

## Success Criteria

- ✅ Support u64, u128, i64, i128 precision
- ✅ Support arbitrary precision via Amari integration
- ✅ No precision loss in input/output
- ✅ Accessible (WCAG 2.1 AA compliance)
- ✅ Performant (< 16ms input latency)
- ✅ WASM-compatible
- ✅ Integration tests with real Amari types
- ✅ Clear migration path from existing Input component

## Implementation Status

### Phase 1: Foundation - COMPLETE (v0.2.0)
- NumberInput component with U64, U128, I64, I128, Decimal(u32) support
- Text-based input with Rust-side validation
- Character filtering and real-time validation
- 6 unit tests for precision validation

### Phase 2: Arbitrary Precision - COMPLETE (v0.3.0)

**Key Decision: rust_decimal vs Amari**

During Phase 2 implementation, we discovered that Amari's type system focuses on:
- `DualNumber` - Automatic differentiation (a + bε where ε² = 0)
- `TropicalNumber` - Max-plus semiring algebra
- `Scalar/Multivector` - Geometric algebra (Clifford algebras)

None of these provide arbitrary-precision decimal arithmetic. The proposal's assumed `amari::Number` type does not exist.

**Solution**: We integrated `rust_decimal` (v1.39) instead, which provides:
- 128-bit fixed-point decimal representation
- Up to 28-29 significant digits
- Exact decimal arithmetic (no floating-point errors)
- `FromStr` parsing for validation
- WASM compatibility

**Usage**:
```rust
// Enable with Cargo feature
[features]
high-precision = ["rust_decimal"]

// Use in code
<NumberInput
    precision=NumberInputPrecision::Arbitrary
    label="High-Precision Value"
/>
```

**Future Amari Integration**:
Amari types may be added in future phases for specialized use cases:
- `DualNumber` inputs for automatic differentiation workflows
- `Scalar` inputs for geometric algebra applications
- These would be additional precision variants, not replacements for rust_decimal

## References

- [rust_decimal Documentation]https://docs.rs/rust_decimal
- [Amari Documentation]https://docs.rs/amari/0.9.10
- [Amari Repository]https://github.com/justinelliottcobb/Amari
- [MDN: Input Type Number Limitations]https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
- [WCAG 2.1 Spinbutton Pattern]https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/