From b549b1678b3efabf0bcc7b5925e2eccfc639d7fa Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:12:31 +0000 Subject: [PATCH 1/6] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Numbers/issues/146 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..557cd40 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-04-14T12:12:31.435Z for PR creation at branch issue-146-24c7ec323aac for issue https://github.com/linksplatform/Numbers/issues/146 \ No newline at end of file From a2c0442038fd1732fe39e5647bf5505e2af1e2d2 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:24:57 +0000 Subject: [PATCH 2/6] feat: add WrappingArithmetic trait and include it in LinkReference bounds Add a composite WrappingArithmetic trait that bundles all six wrapping arithmetic traits from num-traits (WrappingAdd, WrappingSub, WrappingMul, WrappingNeg, WrappingShl, WrappingShr). Include it as a supertrait of LinkReference so downstream crates can use wrapping arithmetic with just T: LinkReference without needing explicit + WrappingAdd bounds. Closes #146 Co-Authored-By: Claude Opus 4.6 --- ...dd_wrapping_arithmetic_to_linkreference.md | 7 + experiments/check_wrapping.rs | 13 ++ experiments/check_wrapping_test.rs | 16 ++ rust/Cargo.lock | 2 +- rust/Cargo.toml | 2 +- rust/src/imp.rs | 37 +++- rust/src/lib.rs | 5 +- rust/tests/traits.rs | 158 +++++++++++++++++- 8 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 changelog.d/20260414_100000_add_wrapping_arithmetic_to_linkreference.md create mode 100644 experiments/check_wrapping.rs create mode 100644 experiments/check_wrapping_test.rs diff --git a/changelog.d/20260414_100000_add_wrapping_arithmetic_to_linkreference.md b/changelog.d/20260414_100000_add_wrapping_arithmetic_to_linkreference.md new file mode 100644 index 0000000..2d2697c --- /dev/null +++ b/changelog.d/20260414_100000_add_wrapping_arithmetic_to_linkreference.md @@ -0,0 +1,7 @@ +--- +bump: minor +--- + +### Added +- `WrappingArithmetic` composite trait bundling `WrappingAdd`, `WrappingSub`, `WrappingMul`, `WrappingNeg`, `WrappingShl`, `WrappingShr` from `num-traits` +- `WrappingArithmetic` as a supertrait of `LinkReference`, enabling downstream crates to use wrapping arithmetic with `T: LinkReference` without additional `+ WrappingAdd` bounds diff --git a/experiments/check_wrapping.rs b/experiments/check_wrapping.rs new file mode 100644 index 0000000..a5119bf --- /dev/null +++ b/experiments/check_wrapping.rs @@ -0,0 +1,13 @@ +use num_traits::ops::wrapping::*; + +fn check() {} + +fn main() { + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + println!("All unsigned types implement all wrapping traits!"); +} diff --git a/experiments/check_wrapping_test.rs b/experiments/check_wrapping_test.rs new file mode 100644 index 0000000..3083a6a --- /dev/null +++ b/experiments/check_wrapping_test.rs @@ -0,0 +1,16 @@ +#[cfg(test)] +mod tests { + use num_traits::ops::wrapping::*; + + fn check_all_wrapping() {} + + #[test] + fn test_all_unsigned_types_implement_wrapping() { + check_all_wrapping::(); + check_all_wrapping::(); + check_all_wrapping::(); + check_all_wrapping::(); + check_all_wrapping::(); + check_all_wrapping::(); + } +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 43edbb8..76af936 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "platform-num" -version = "0.6.0" +version = "0.7.0" dependencies = [ "num-traits", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index faaab05..b9ad65c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "platform-num" -version = "0.7.0" +version = "0.8.0" edition = "2024" rust-version = "1.85" authors = ["uselesssgoddess", "Linksplatform Team "] diff --git a/rust/src/imp.rs b/rust/src/imp.rs index bb59315..8b1575f 100644 --- a/rust/src/imp.rs +++ b/rust/src/imp.rs @@ -2,6 +2,9 @@ use std::fmt::{Debug, Display}; use std::hash::Hash; use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed, ToPrimitive, Unsigned}; +use num_traits::ops::wrapping::{ + WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, +}; /// A base numeric trait combining `PrimInt`, `Default`, `Debug`, /// `AsPrimitive`, and `ToPrimitive`. @@ -138,10 +141,40 @@ macro_rules! max_value_impl { for_each_integer_type!(max_value_impl); +/// A composite trait for wrapping arithmetic operations. +/// +/// Combines all wrapping arithmetic traits from `num-traits`: +/// [`WrappingAdd`], [`WrappingSub`], [`WrappingMul`], +/// [`WrappingNeg`], [`WrappingShl`], and [`WrappingShr`]. +/// +/// Implemented for all primitive integer types. +/// +/// # Examples +/// +/// ``` +/// use platform_num::WrappingArithmetic; +/// +/// fn wrapping_increment(a: &T, b: &T) -> T { +/// a.wrapping_add(b) +/// } +/// +/// assert_eq!(wrapping_increment(&u8::MAX, &1u8), 0u8); +/// ``` +pub trait WrappingArithmetic: + WrappingAdd + WrappingSub + WrappingMul + WrappingNeg + WrappingShl + WrappingShr +{ +} + +impl + WrappingArithmetic for All +{ +} + /// A composite trait for types that can be used as link identifiers. /// /// Combines [`Number`], `Unsigned`, [`ToSigned`], [`MaxValue`], -/// `FromPrimitive`, `TryFrom`/`TryInto` for all integer types, +/// [`WrappingArithmetic`], `FromPrimitive`, +/// `TryFrom`/`TryInto` for all integer types, /// `Debug`, `Display`, `Hash`, `Send`, `Sync`, and `'static`. /// /// Implemented for `u8`, `u16`, `u32`, `u64`, `u128`, and `usize`. @@ -171,6 +204,7 @@ Sized + Unsigned + ToSigned + MaxValue ++ WrappingArithmetic + FromPrimitive + TryFrom + TryFrom @@ -218,6 +252,7 @@ impl< + Unsigned + ToSigned + MaxValue + + WrappingArithmetic + FromPrimitive + TryFrom + TryFrom diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b57ad59..491ab3a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -15,7 +15,8 @@ //! | [`SignedNumber`] | Extends [`Number`] with signed operations (`Signed + FromPrimitive`) | //! | [`ToSigned`] | Converts an unsigned type to its signed counterpart (e.g. `u32` → `i32`) | //! | [`MaxValue`] | Provides a `MAX` associated constant for every primitive integer type | -//! | [`LinkReference`] | Composite trait for link identifiers — unsigned, hashable, displayable, thread-safe, with `TryFrom`/`TryInto` for all integer types | +//! | [`WrappingArithmetic`] | Composite trait for wrapping arithmetic — bundles `WrappingAdd`, `WrappingSub`, `WrappingMul`, `WrappingNeg`, `WrappingShl`, `WrappingShr` | +//! | [`LinkReference`] | Composite trait for link identifiers — unsigned, hashable, displayable, thread-safe, with wrapping arithmetic and `TryFrom`/`TryInto` for all integer types | //! //! ## Example //! @@ -31,4 +32,4 @@ mod imp; -pub use imp::{LinkReference, MaxValue, Number, SignedNumber, ToSigned}; +pub use imp::{LinkReference, MaxValue, Number, SignedNumber, ToSigned, WrappingArithmetic}; diff --git a/rust/tests/traits.rs b/rust/tests/traits.rs index 98583fa..b4b2d5f 100644 --- a/rust/tests/traits.rs +++ b/rust/tests/traits.rs @@ -1,4 +1,4 @@ -use platform_num::{LinkReference, MaxValue, Number, SignedNumber, ToSigned}; +use platform_num::{LinkReference, MaxValue, Number, SignedNumber, ToSigned, WrappingArithmetic}; // ========================================== // Tests for Number trait @@ -310,6 +310,162 @@ fn test_max_value_usize() { assert_eq!(usize::MAX, ::MAX); } +// ========================================== +// Tests for WrappingArithmetic trait +// ========================================== + +#[test] +fn test_wrapping_arithmetic_for_u8() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0u8); +} + +#[test] +fn test_wrapping_arithmetic_for_u16() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0u16); +} + +#[test] +fn test_wrapping_arithmetic_for_u32() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0u32); +} + +#[test] +fn test_wrapping_arithmetic_for_u64() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0u64); +} + +#[test] +fn test_wrapping_arithmetic_for_u128() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0u128); +} + +#[test] +fn test_wrapping_arithmetic_for_usize() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0usize); +} + +#[test] +fn test_wrapping_arithmetic_for_signed_types() { + fn assert_wrapping(_val: T) {} + assert_wrapping(0i8); + assert_wrapping(0i16); + assert_wrapping(0i32); + assert_wrapping(0i64); + assert_wrapping(0i128); + assert_wrapping(0isize); +} + +#[test] +fn test_wrapping_add_overflow() { + fn wrapping_add(a: &T, b: &T) -> T { + a.wrapping_add(b) + } + assert_eq!(wrapping_add(&u8::MAX, &1u8), 0u8); + assert_eq!(wrapping_add(&u16::MAX, &1u16), 0u16); + assert_eq!(wrapping_add(&u32::MAX, &1u32), 0u32); + assert_eq!(wrapping_add(&u64::MAX, &1u64), 0u64); + assert_eq!(wrapping_add(&u128::MAX, &1u128), 0u128); + assert_eq!(wrapping_add(&usize::MAX, &1usize), 0usize); +} + +#[test] +fn test_wrapping_sub_underflow() { + fn wrapping_sub(a: &T, b: &T) -> T { + a.wrapping_sub(b) + } + assert_eq!(wrapping_sub(&0u8, &1u8), u8::MAX); + assert_eq!(wrapping_sub(&0u16, &1u16), u16::MAX); + assert_eq!(wrapping_sub(&0u32, &1u32), u32::MAX); + assert_eq!(wrapping_sub(&0u64, &1u64), u64::MAX); + assert_eq!(wrapping_sub(&0u128, &1u128), u128::MAX); + assert_eq!(wrapping_sub(&0usize, &1usize), usize::MAX); +} + +#[test] +fn test_wrapping_mul_overflow() { + fn wrapping_mul(a: &T, b: &T) -> T { + a.wrapping_mul(b) + } + assert_eq!(wrapping_mul(&u8::MAX, &2u8), u8::MAX - 1); + assert_eq!(wrapping_mul(&u32::MAX, &2u32), u32::MAX - 1); +} + +#[test] +fn test_wrapping_neg() { + fn wrapping_neg(a: &T) -> T { + a.wrapping_neg() + } + assert_eq!(wrapping_neg(&0u8), 0u8); + assert_eq!(wrapping_neg(&1u8), u8::MAX); + assert_eq!(wrapping_neg(&0u32), 0u32); + assert_eq!(wrapping_neg(&1u32), u32::MAX); +} + +#[test] +fn test_wrapping_shl() { + fn wrapping_shl(a: &T, b: u32) -> T { + a.wrapping_shl(b) + } + assert_eq!(wrapping_shl(&1u8, 7), 128u8); + assert_eq!(wrapping_shl(&1u32, 31), 1u32 << 31); +} + +#[test] +fn test_wrapping_shr() { + fn wrapping_shr(a: &T, b: u32) -> T { + a.wrapping_shr(b) + } + assert_eq!(wrapping_shr(&128u8, 7), 1u8); + assert_eq!(wrapping_shr(&u32::MAX, 16), 0xFFFFu32); +} + +// ========================================== +// Tests for LinkReference wrapping arithmetic +// ========================================== + +#[test] +fn test_link_reference_wrapping_add() { + fn wrapping_add(a: &T, b: &T) -> T { + a.wrapping_add(b) + } + assert_eq!(wrapping_add(&u8::MAX, &1u8), 0u8); + assert_eq!(wrapping_add(&u32::MAX, &1u32), 0u32); + assert_eq!(wrapping_add(&u64::MAX, &1u64), 0u64); + assert_eq!(wrapping_add(&u128::MAX, &1u128), 0u128); +} + +#[test] +fn test_link_reference_wrapping_sub() { + fn wrapping_sub(a: &T, b: &T) -> T { + a.wrapping_sub(b) + } + assert_eq!(wrapping_sub(&0u8, &1u8), u8::MAX); + assert_eq!(wrapping_sub(&0u32, &1u32), u32::MAX); + assert_eq!(wrapping_sub(&0u64, &1u64), u64::MAX); + assert_eq!(wrapping_sub(&0u128, &1u128), u128::MAX); +} + +#[test] +fn test_link_reference_wrapping_arithmetic_no_extra_bounds() { + fn wrapping_ops(a: T, b: T) -> (T, T, T) { + ( + a.wrapping_add(&b), + a.wrapping_sub(&b), + a.wrapping_mul(&b), + ) + } + let (add, sub, mul) = wrapping_ops(u32::MAX, 1u32); + assert_eq!(add, 0u32); + assert_eq!(sub, u32::MAX - 1); + assert_eq!(mul, u32::MAX); +} + // ========================================== // Tests for LinkReference trait // ========================================== From c1c9133234439eab4478c458359c0c86a4dde2c9 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:26:22 +0000 Subject: [PATCH 3/6] docs: add case study analysis for issue #146 Compile raw data from related issues (data-rs#16, data-rs#17, doublets-rs#47) and write analysis covering timeline, root causes, solution rationale, and downstream impact. Co-Authored-By: Claude Opus 4.6 --- docs/case-studies/issue-146/analysis.md | 107 ++++++++++++++++++ .../issue-146/data-rs-issue-16-raw.json | 1 + .../issue-146/data-rs-pr-17-raw.json | 1 + .../issue-146/doublets-rs-issue-47-raw.json | 1 + .../case-studies/issue-146/issue-146-raw.json | 1 + 5 files changed, 111 insertions(+) create mode 100644 docs/case-studies/issue-146/analysis.md create mode 100644 docs/case-studies/issue-146/data-rs-issue-16-raw.json create mode 100644 docs/case-studies/issue-146/data-rs-pr-17-raw.json create mode 100644 docs/case-studies/issue-146/doublets-rs-issue-47-raw.json create mode 100644 docs/case-studies/issue-146/issue-146-raw.json diff --git a/docs/case-studies/issue-146/analysis.md b/docs/case-studies/issue-146/analysis.md new file mode 100644 index 0000000..914ffdb --- /dev/null +++ b/docs/case-studies/issue-146/analysis.md @@ -0,0 +1,107 @@ +# Case Study: Issue #146 — Add WrappingAdd to LinkReference Trait Bounds + +## Timeline / Sequence of Events + +| Date | Event | Repository | +|------|-------|-----------| +| 2026-04-13 | doublets-rs#47 opened — quality audit requiring latest dependencies and trait unification | linksplatform/doublets-rs | +| 2026-04-14 | data-rs#16 opened — Replace funty dependency with platform-num (`LinkReference`) | linksplatform/data-rs | +| 2026-04-14 | data-rs#17 PR opened — Implements the replacement, discovers `WrappingAdd` gap | linksplatform/data-rs | +| 2026-04-14 | Numbers#146 opened — Request to add `WrappingAdd` to `LinkReference` trait bounds | linksplatform/Numbers | +| 2026-04-14 | Numbers#147 PR opened — Implementation of `WrappingArithmetic` composite trait | linksplatform/Numbers | + +## Requirements from the Issue + +### R1: Add wrapping arithmetic to `LinkReference` supertraits (Original request) +The original issue requested adding `WrappingAdd` from `num-traits` to `LinkReference` so downstream crates don't need explicit `+ WrappingAdd` bounds. + +### R2: Better naming than `WrappingAdd` (From comment by @konard) +The maintainer disliked the name `WrappingAdd` and suggested something like `AdditionCapable`. They asked to check if similar traits already exist in `num-traits`. + +### R3: Support similar operations for other arithmetic ops (From comment by @konard) +Not just `WrappingAdd` — all wrapping arithmetic operations normally supported by numeric types should be included. + +### R4: Compile data and perform deep case study analysis (From comment by @konard) +Download all logs and data related to the issue, compile to `./docs/case-studies/issue-146` folder, reconstruct timeline, find root causes, propose solutions. + +## Root Cause Analysis + +### Root Cause 1: Trait ecosystem fragmentation +The Rust ecosystem has two competing unsigned integer trait crates: +- `funty` (provides `funty::Unsigned`) +- `num-traits` (provides `num_traits::Unsigned`) + +`platform-data` used `funty::Unsigned` while `platform-num` (Numbers) used `num_traits::Unsigned`. These are **different traits from different crates**, even though they represent the same concept. This made it impossible to write unified `T: data::LinkType + trees::LinkType` bounds. + +### Root Cause 2: Missing wrapping arithmetic in `PrimInt` +The `PrimInt` trait from `num-traits` (which is a supertrait of `Number`) provides **non-wrapping** arithmetic. The wrapping variants (`WrappingAdd`, `WrappingSub`, `WrappingMul`, `WrappingNeg`, `WrappingShl`, `WrappingShr`) are separate traits in `num_traits::ops::wrapping`. This means code that needs wrapping semantics (like `Hybrid` in `platform-data`) must add extra trait bounds. + +### Root Cause 3: No composite wrapping trait in `num-traits` +Unlike `PrimInt` which bundles many non-wrapping operations, `num-traits` does **not** provide a composite trait that bundles all wrapping operations. Each wrapping operation is a separate trait, leading to verbose bounds like `T: WrappingAdd + WrappingSub + WrappingMul + WrappingNeg`. + +## Solution Implemented + +### Approach: Composite `WrappingArithmetic` trait + +Created a new `WrappingArithmetic` composite trait in `platform-num` that bundles all six wrapping arithmetic traits from `num-traits`: + +```rust +pub trait WrappingArithmetic: + WrappingAdd + WrappingSub + WrappingMul + WrappingNeg + WrappingShl + WrappingShr +{} +``` + +With a blanket implementation for any type that implements all six traits. + +Then added `WrappingArithmetic` as a supertrait of `LinkReference`. + +### Why this approach + +1. **Addresses naming concern**: `WrappingArithmetic` is descriptive and covers all operations, not just addition +2. **Broader than requested**: Includes all 6 wrapping traits, not just `WrappingAdd` +3. **Compatible addition**: All unsigned primitive integer types already implement all 6 wrapping traits +4. **Follows existing patterns**: Same composite trait pattern as `Number` (which bundles `PrimInt + Default + Debug + AsPrimitive + ToPrimitive`) +5. **Preserves ecosystem naming**: Individual traits retain their `num-traits` names; the composite provides a convenient bundle + +### Alternatives Considered + +| Alternative | Pros | Cons | +|------------|------|------| +| Add only `WrappingAdd` | Minimal change | Doesn't address other wrapping ops; name issue remains | +| Add individual traits directly to `LinkReference` | No new trait needed | Longer trait definition; no reusable composite | +| Create `AdditionCapable` trait | Addresses naming | Too narrow; name implies only addition | +| Re-export `WrappingAdd` as a different name | Simple | Confusing; diverges from ecosystem naming | + +## Impact on Downstream Crates + +### `platform-data` (data-rs#17) +Currently the PR uses `T: LinkReference + WrappingAdd` everywhere. After this change is released as `platform-num` 0.8.0, `data-rs` can: +1. Remove the explicit `num-traits` dependency +2. Replace `T: LinkReference + WrappingAdd` with just `T: LinkReference` +3. All wrapping operations will be available through the `LinkReference` bound + +### `doublets-rs` +Can use a single `T: LinkReference` bound for all numeric operations including wrapping arithmetic. + +## Verification + +- All 112 unit tests pass (96 existing + 16 new wrapping arithmetic tests) +- All 7 doc tests pass (including new `WrappingArithmetic` doctest) +- `cargo clippy --all-targets` reports zero warnings +- Verified that all 6 wrapping traits are implemented for `u8`, `u16`, `u32`, `u64`, `u128`, `usize` + +## Related Issues and PRs + +| Reference | Repository | Description | +|-----------|-----------|-------------| +| [#146](https://github.com/linksplatform/Numbers/issues/146) | Numbers | This issue — Add WrappingAdd to LinkReference | +| [#147](https://github.com/linksplatform/Numbers/pull/147) | Numbers | This PR — Implementation | +| [#16](https://github.com/linksplatform/data-rs/issues/16) | data-rs | Replace funty with platform-num | +| [#17](https://github.com/linksplatform/data-rs/pull/17) | data-rs | PR implementing funty replacement | +| [#47](https://github.com/linksplatform/doublets-rs/issues/47) | doublets-rs | Quality audit driving the unification | + +## References + +- [num-traits wrapping module documentation](https://docs.rs/num-traits/latest/num_traits/ops/wrapping/) +- [PrimInt trait documentation](https://docs.rs/num-traits/latest/num_traits/int/trait.PrimInt.html) — provides non-wrapping arithmetic +- [Rust RFC #1530](https://github.com/rust-lang/rfcs/issues/1530) — Discussion on WrappingAdd, WrappingSub, WrappingMul, WrappingDiv traits diff --git a/docs/case-studies/issue-146/data-rs-issue-16-raw.json b/docs/case-studies/issue-146/data-rs-issue-16-raw.json new file mode 100644 index 0000000..dc56557 --- /dev/null +++ b/docs/case-studies/issue-146/data-rs-issue-16-raw.json @@ -0,0 +1 @@ +{"body":"## Summary\n\nReplace `funty::Unsigned` and `FuntyPart` in the `LinkType` trait definition with `LinkReference` from `platform-num` (Numbers), eliminating the `funty` dependency entirely.\n\n## Current state\n\n`platform-data` 1.0.0 defines `LinkType` as:\n\n```rust\npub trait LinkType:\n Unsigned // from funty 2.0.0\n + FuntyPart // local trait wrapping TryFrom\n + TryFrom\n + TryFrom\n // ... all integer TryFrom/TryInto bounds\n{}\n```\n\nThis depends on `funty` 2.0.0 for the `Unsigned` trait. Meanwhile, `platform-trees` uses `num_traits::Unsigned` (via `platform-num::Number`), which is a **different trait** from a different crate. This creates incompatibility at the generic level — you can't write `T: data::LinkType + trees::LinkType` without dual bounds because neither implies the other.\n\n## Proposed change\n\nOnce linksplatform/Numbers#144 is implemented (adding `TryFrom`/`TryInto` bounds and `funty()` method to `LinkReference`), refactor `LinkType` to:\n\n```rust\npub trait LinkType: LinkReference {}\n```\n\nOr simply replace `LinkType` with `LinkReference` throughout the crate.\n\nThis would:\n1. **Remove `funty` from dependencies** — `Unsigned` from `num_traits` (included in `LinkReference` via `Number`) replaces `funty::Unsigned`\n2. **Remove `FuntyPart` trait** — the `funty()` method will be on `LinkReference` directly\n3. **Remove all explicit `TryFrom`/`TryInto` bounds** — they'll be part of `LinkReference`\n4. **Unify with `platform-trees`** — both crates will use the same base trait\n\n## Impact on downstream crates\n\n- `doublets-rs` can use a single `T: LinkReference` bound instead of `T: data::LinkType + trees::LinkType`\n- Any crate depending on `platform-data` for `LinkType` would get the same concrete type support\n\n## Related\n\n- linksplatform/Numbers#144 — Add required bounds to `LinkReference`\n- linksplatform/trees-rs issue — Corresponding change for trees\n- linksplatform/doublets-rs#47 — Original issue driving this unification\n- linksplatform/doublets-rs#48 — PR with full analysis","createdAt":"2026-04-14T10:12:34Z","state":"OPEN","title":"Replace funty dependency with platform-num (Numbers) for LinkType trait"} diff --git a/docs/case-studies/issue-146/data-rs-pr-17-raw.json b/docs/case-studies/issue-146/data-rs-pr-17-raw.json new file mode 100644 index 0000000..4b6708c --- /dev/null +++ b/docs/case-studies/issue-146/data-rs-pr-17-raw.json @@ -0,0 +1 @@ +{"body":"## Summary\n\n- Replaced `funty` dependency with `platform-num` (`LinkReference` trait) and `num-traits` (`WrappingAdd`)\n- **Removed `LinkType` trait entirely** — all bounds now use `LinkReference` directly\n- Replaced all `T::funty(n)` calls with `T::from_byte(n)` across the codebase\n- Re-exported `LinkReference` from crate root for downstream convenience\n\n## Motivation\n\n`platform-data` used `funty::Unsigned` while `platform-trees` used `num_traits::Unsigned` (via `platform-num::Number`). These are **different traits from different crates**, making it impossible to write `T: data::LinkType + trees::LinkType` without dual bounds. With `LinkReference` from `platform-num` 0.7.0, both crates now share the same base trait, enabling downstream crates like `doublets-rs` to use a single `T: LinkReference` bound.\n\nPer [review feedback](https://github.com/linksplatform/data-rs/pull/17#issuecomment-4243668806), `LinkType` has been removed entirely rather than kept as a thin wrapper.\n\n## Changes\n\n| File | Change |\n|---|---|\n| `Cargo.toml` | `funty` → `platform-num` + `num-traits` |\n| `src/link_type.rs` | **Deleted** — `LinkType` trait removed entirely |\n| `src/lib.rs` | Removed `mod link_type` and `pub use LinkType`; kept `pub use LinkReference` |\n| `src/hybrid.rs` | Uses `LinkReference + WrappingAdd` directly |\n| `src/constants.rs` | Uses `LinkReference + WrappingAdd` directly |\n| `src/converters.rs` | Uses `LinkReference + WrappingAdd` directly |\n| `src/links.rs` | Uses `LinkReference + WrappingAdd` directly |\n| `README.md` | Updated docs to reference `LinkReference` instead of `LinkType` |\n| `changelog.d/` | Updated breaking change fragment |\n\n## Breaking changes\n\n- `LinkType` trait **removed** (replaced by `LinkReference` from `platform-num`)\n- `FuntyPart` trait removed (replaced by `LinkReference::from_byte`)\n- `T::funty(n)` → `T::from_byte(n)` migration required for downstream users\n- All generic bounds changed from `T: LinkType` to `T: LinkReference + WrappingAdd`\n\n## Follow-up\n\n- linksplatform/Numbers#146 — Filed issue to add `WrappingAdd` to `LinkReference` trait bounds in `platform-num`, which would allow removing the explicit `+ WrappingAdd` bounds and the `num-traits` dependency\n\n## Test plan\n\n- [x] `cargo build` compiles cleanly\n- [x] `cargo clippy --all-targets` passes with zero warnings\n- [x] `cargo test --all-features` passes (all 94 tests + 1 doctest)\n- [ ] CI pipeline passes on GitHub Actions\n\nCloses #16\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","createdAt":"2026-04-14T11:37:40Z","files":[{"path":"Cargo.lock","additions":27,"deletions":8,"changeType":"MODIFIED"},{"path":"Cargo.toml","additions":2,"deletions":1,"changeType":"MODIFIED"},{"path":"README.md","additions":5,"deletions":4,"changeType":"MODIFIED"},{"path":"changelog.d/20260414_replace_funty_with_platform_num.md","additions":14,"deletions":0,"changeType":"ADDED"},{"path":"src/constants.rs","additions":18,"deletions":16,"changeType":"MODIFIED"},{"path":"src/converters.rs","additions":5,"deletions":3,"changeType":"MODIFIED"},{"path":"src/hybrid.rs","additions":10,"deletions":7,"changeType":"MODIFIED"},{"path":"src/lib.rs","additions":1,"deletions":2,"changeType":"MODIFIED"},{"path":"src/link_type.rs","additions":0,"deletions":87,"changeType":"DELETED"},{"path":"src/links.rs","additions":5,"deletions":3,"changeType":"MODIFIED"}],"state":"OPEN","title":"feat!: replace funty with platform-num, remove LinkType"} diff --git a/docs/case-studies/issue-146/doublets-rs-issue-47-raw.json b/docs/case-studies/issue-146/doublets-rs-issue-47-raw.json new file mode 100644 index 0000000..316be45 --- /dev/null +++ b/docs/case-studies/issue-146/doublets-rs-issue-47-raw.json @@ -0,0 +1 @@ +{"body":"Double check that our code is highest possible quality implementation for the same API it supports now. We should use all the latest versions of dependencies, and latest stable version of rust.\n\nMake sure docs are in sync with code, and fully describe all the features we have in the code.\n\nDouble check if it possible to increase tests coverage and docs coverage. And also if it possible to make automated documentation generation as we do in https://github.com/linksplatform/Numbers and https://github.com/linksplatform/trees-rs, and https://github.com/linksplatform/mem-rs, so the final website published in GitHub Pages contains generated documentation for all supported languages. Also code should contain all comments necessary to generate high quality documentation.\n\nWe should depend on real cargo/crates packages, not source code, so all submodules with dependencies source code can be removed.\n\nAlso no tests should be in src folder, use tests folder for tests.\n\nPlease fully support best practices of CI/CD from:\n- https://github.com/linksplatform/mem-rs\n- https://github.com/linksplatform/trees-rs\n- https://github.com/linksplatform/Numbers\n- https://github.com/link-foundation/rust-ai-driven-development-pipeline-template\n\nIf something is missing at https://github.com/link-foundation/rust-ai-driven-development-pipeline-template (make sure to compare full file tree for CI/CD) report the issue there.\n\nSo https://github.com/link-foundation/rust-ai-driven-development-pipeline-template will accumulate all the best practices of CI/CD for Rust from all our projects, so it will have highest possible quality and we will need less iterations to make our CI/CD work in all projects.\n\nWe need to collect data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), list of each and all requirements from the issue, and propose possible solutions and solution plans for each requirement (we should also check known existing components/libraries, that solve similar problem or can help in solutions).","createdAt":"2026-04-13T08:55:31Z","state":"OPEN","title":"Double check we don't depend on any non stable features of rust and use all the latest best practices and versions of rust in the code"} diff --git a/docs/case-studies/issue-146/issue-146-raw.json b/docs/case-studies/issue-146/issue-146-raw.json new file mode 100644 index 0000000..47b839e --- /dev/null +++ b/docs/case-studies/issue-146/issue-146-raw.json @@ -0,0 +1 @@ +{"author":{"id":"MDQ6VXNlcjE0MzE5MDQ=","is_bot":false,"login":"konard","name":"Konstantin Diachenko"},"body":"## Summary\n\n`LinkReference` should include `num_traits::WrappingAdd` (and potentially other wrapping arithmetic traits) in its supertraits.\n\n## Motivation\n\nAfter removing the `LinkType` trait from `platform-data` (linksplatform/data-rs#16), all bounds that were on `LinkType` need to come from `LinkReference` directly. The `WrappingAdd` trait is needed for wrapping unsigned arithmetic in `Hybrid` (used for internal/external link reference encoding).\n\nCurrently, `platform-data` must add `num-traits` as an explicit dependency and add `+ WrappingAdd` bounds alongside every `LinkReference` bound. If `LinkReference` included `WrappingAdd`, downstream crates could use `T: LinkReference` without additional trait bounds.\n\n## Proposed change\n\nAdd `WrappingAdd` (from `num-traits`) to the `LinkReference` trait definition:\n\n```rust\npub trait LinkReference:\n // ... existing bounds ...\n + WrappingAdd\n{}\n```\n\nSince `WrappingAdd` is implemented for all unsigned primitive integer types (the same types `LinkReference` is implemented for), this is a compatible addition.\n\n## Context\n\n- linksplatform/data-rs#16 — Replace funty with platform-num\n- linksplatform/data-rs#17 — PR implementing the change","comments":[{"id":"IC_kwDOC8wDk8788pyG","author":{"login":"konard"},"authorAssociation":"MEMBER","body":"I don't like the name of `WrappingAdd`, instead we should use something like `AdditionCapable`, check something similar already exists in num traites and we really need, and check what the best practice naming for that.\n\nWe also support similar for other operations that are usually supported by numeric types.\n\nWe need to download all logs and data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), in which we will reconstruct timeline/sequence of events, list of each and all requirements from the issue, find root causes of the each problem, and propose possible solutions and solution plans for each requirement (we should also check known existing components/libraries, that solve similar problem or can help in solutions).\n\nIf there is not enough data to find actual root cause, add debug output and verbose mode if not present, that will allow us to find root cause on next iteration.\n\nIf issue related to any other repository/project, where we can report issues on GitHub, please do so. Each issue must contain reproducible examples, workarounds and suggestions for fix the issue in code.","createdAt":"2026-04-14T12:11:05Z","includesCreatedEdit":true,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/linksplatform/Numbers/issues/146#issuecomment-4243758214","viewerDidAuthor":true}],"createdAt":"2026-04-14T12:08:46Z","title":"Add WrappingAdd to LinkReference trait bounds"} From 9884b7253591d07a7315e414bf2930a4057e80d2 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:30:50 +0000 Subject: [PATCH 4/6] fix: revert manual version change, fix rustfmt import ordering CI manages versions automatically via changelog fragments. Fix import order to satisfy rustfmt (ops::wrapping before top-level). Co-Authored-By: Claude Opus 4.6 --- rust/Cargo.toml | 2 +- rust/src/imp.rs | 2 +- rust/tests/traits.rs | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b9ad65c..faaab05 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "platform-num" -version = "0.8.0" +version = "0.7.0" edition = "2024" rust-version = "1.85" authors = ["uselesssgoddess", "Linksplatform Team "] diff --git a/rust/src/imp.rs b/rust/src/imp.rs index 8b1575f..edd7279 100644 --- a/rust/src/imp.rs +++ b/rust/src/imp.rs @@ -1,10 +1,10 @@ use std::fmt::{Debug, Display}; use std::hash::Hash; -use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed, ToPrimitive, Unsigned}; use num_traits::ops::wrapping::{ WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, }; +use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed, ToPrimitive, Unsigned}; /// A base numeric trait combining `PrimInt`, `Default`, `Debug`, /// `AsPrimitive`, and `ToPrimitive`. diff --git a/rust/tests/traits.rs b/rust/tests/traits.rs index b4b2d5f..91b8cfa 100644 --- a/rust/tests/traits.rs +++ b/rust/tests/traits.rs @@ -454,11 +454,7 @@ fn test_link_reference_wrapping_sub() { #[test] fn test_link_reference_wrapping_arithmetic_no_extra_bounds() { fn wrapping_ops(a: T, b: T) -> (T, T, T) { - ( - a.wrapping_add(&b), - a.wrapping_sub(&b), - a.wrapping_mul(&b), - ) + (a.wrapping_add(&b), a.wrapping_sub(&b), a.wrapping_mul(&b)) } let (add, sub, mul) = wrapping_ops(u32::MAX, 1u32); assert_eq!(add, 0u32); From c0d8c936578a8d37bb11bc93132eea017823aa76 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:37:18 +0000 Subject: [PATCH 5/6] Revert "Initial commit with task details" This reverts commit b549b1678b3efabf0bcc7b5925e2eccfc639d7fa. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 557cd40..0000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-04-14T12:12:31.435Z for PR creation at branch issue-146-24c7ec323aac for issue https://github.com/linksplatform/Numbers/issues/146 \ No newline at end of file From 437b9e7dd2f53e2059854698552d9bfd4f580d91 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 12:41:09 +0000 Subject: [PATCH 6/6] feat: re-export all num-traits used in supertraits Downstream crates (data-rs, tree-rs, doublets-rs) can now import PrimInt, AsPrimitive, ToPrimitive, FromPrimitive, Unsigned, Signed, and all six Wrapping* traits directly from platform-num, eliminating the need for a separate num-traits dependency and avoiding version drift risk. Addresses #141 Co-Authored-By: Claude Opus 4.6 --- rust/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 491ab3a..e2e871c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -18,6 +18,22 @@ //! | [`WrappingArithmetic`] | Composite trait for wrapping arithmetic — bundles `WrappingAdd`, `WrappingSub`, `WrappingMul`, `WrappingNeg`, `WrappingShl`, `WrappingShr` | //! | [`LinkReference`] | Composite trait for link identifiers — unsigned, hashable, displayable, thread-safe, with wrapping arithmetic and `TryFrom`/`TryInto` for all integer types | //! +//! ## Re-exported `num-traits` +//! +//! All `num-traits` traits that appear in this crate's supertraits are +//! re-exported so that downstream crates can use them without adding +//! `num-traits` as a direct dependency: +//! +//! | Re-export | Used in | +//! |-----------|---------| +//! | [`PrimInt`] | [`Number`] | +//! | [`AsPrimitive`] | [`Number`] | +//! | [`ToPrimitive`] | [`Number`] | +//! | [`FromPrimitive`] | [`SignedNumber`], [`LinkReference`] | +//! | [`Signed`] | [`SignedNumber`] | +//! | [`Unsigned`] | [`LinkReference`] | +//! | [`WrappingAdd`], [`WrappingSub`], [`WrappingMul`], [`WrappingNeg`], [`WrappingShl`], [`WrappingShr`] | [`WrappingArithmetic`] | +//! //! ## Example //! //! ``` @@ -33,3 +49,8 @@ mod imp; pub use imp::{LinkReference, MaxValue, Number, SignedNumber, ToSigned, WrappingArithmetic}; + +pub use num_traits::ops::wrapping::{ + WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, +}; +pub use num_traits::{AsPrimitive, FromPrimitive, PrimInt, Signed, ToPrimitive, Unsigned};