Skip to content

Conversation

@immrsd
Copy link
Collaborator

@immrsd immrsd commented Nov 19, 2025

Resolves #21

PR Checklist

  • Tests
  • Documentation
  • Changelog

Summary by CodeRabbit

  • New Features

    • Added square root operations for all unsigned integer types with configurable rounding modes (down, up, nearest), enabling flexible mathematical computations with precise control over result rounding.
  • Tests

    • Comprehensive test coverage added for square root operations across all integer widths, including edge cases and boundary conditions.

@immrsd immrsd self-assigned this Nov 19, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Implements square root functionality across all unsigned integer types (u8–u256) with configurable rounding modes. The implementation includes a core floor-square-root algorithm using Newton-Raphson iteration, a rounding utility for multiple rounding strategies, and public API functions for each integer type.

Changes

Cohort / File(s) Summary
Core sqrt algorithm and rounding
math/core/sources/internal/common.move
Adds sqrt_floor(a: u256): u256 implementing integer Newton-Raphson method with edge-case handling and iterative refinement to compute floor(sqrt(a))
Sqrt macro and rounding logic
math/core/sources/internal/macros.move
Introduces sqrt<$Int> macro normalizing to u256, computing floor via sqrt_floor, and applying rounding; adds round_sqrt_result helper for rounding logic across down/up/nearest modes
Public sqrt API exposure
math/core/sources/u8.move, u16.move, u32.move, u64.move, u128.move, u256.move
Each adds public sqrt(value: $Type, rounding_mode: RoundingMode): $Type function delegating to macros::sqrt! macro
Core sqrt tests
math/core/tests/common_tests.move
Adds comprehensive sqrt_floor test coverage for all integer widths across zero, perfect squares, non-perfect squares, and max values
Macro and type-specific sqrt tests
math/core/tests/macros_tests.move, u8_tests.move, u16_tests.move, u32_tests.move, u64_tests.move, u128_tests.move, u256_tests.move
Extensive test suites for sqrt macro and each type, covering zero handling, perfect squares, all rounding modes (down/up/nearest), edge cases, powers of two, midpoint behavior, and max values

Sequence Diagram

sequenceDiagram
    participant User
    participant PublicAPI as sqrt(value, rounding_mode)<br/>on u8/u16/u32/u64/u128/u256
    participant Macro as sqrt!<br/>macro
    participant Common as sqrt_floor(a: u256)<br/>in common.move
    participant Round as round_sqrt_result(...)

    User->>PublicAPI: Call sqrt(value, rounding_mode)
    PublicAPI->>Macro: Delegate to macros::sqrt!
    Macro->>Macro: Normalize value to u256
    Macro->>Common: Call sqrt_floor(normalized_value)
    Common->>Common: Edge-case check (a <= 1)
    Common->>Common: Initial high-precision estimate
    Common->>Common: Iterative Newton-Raphson refinement
    Common-->>Macro: Return floor(sqrt(a))
    Macro->>Round: Call round_sqrt_result(value, floor_result, rounding_mode)
    Round->>Round: Apply rounding logic (down/up/nearest)<br/>and handle perfect squares
    Round-->>Macro: Return rounded sqrt result
    Macro-->>PublicAPI: Return result cast to original type
    PublicAPI-->>User: Return sqrt(value, rounding_mode)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas requiring extra attention:
    • math/core/sources/internal/common.move: Review correctness of Newton-Raphson iteration logic, convergence bounds, and edge-case handling for u256 floor-square-root computation
    • math/core/sources/internal/macros.move: Verify rounding logic in round_sqrt_result for all three modes (down/up/nearest) and ensure correct handling of perfect squares and overflow scenarios
    • Cross-type consistency: Validate that the macro correctly normalizes and denormalizes across all integer widths without precision loss or unexpected truncation

Suggested reviewers

  • bidzyyys
  • ericnordelo

Poem

🐰 A rabbit hops through square roots grand,
Where Newton's method takes a stand,
From u8 to u256 so wide,
With rounding modes as trusty guide,
Now every number finds its square,
Computed with the utmost care! 🌱

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description mentions issue #21 and includes the PR checklist, but lacks any detailed explanation of the changes, rationale, or implementation approach. Add context describing what sqrt implementation was added, which integer types are supported, and explain the rounding mode approach used.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements sqrt function for multiple unsigned integer types (u8-u256) with comprehensive tests and rounding support, meeting the requirement to implement a sqrt function.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the sqrt feature: core sqrt_floor algorithm, macro-based sqrt for different types, public APIs, and comprehensive test coverage across all changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately summarizes the main change: adding a sqrt implementation with comprehensive tests across multiple integer types.

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
math/core/sources/internal/common.move (1)

33-144: LGTM! Well-implemented integer square root using Newton-Raphson.

The implementation is mathematically sound and well-documented. The Newton-Raphson method with initial bit-length-based estimation and iterative refinement correctly computes floor(sqrt(a)). The extensive comments explain the convergence properties clearly.

One minor typo on Line 136: "we know have" should be "we now have".

Apply this diff to fix the typo:

-    // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
+    // Because e ≤ 128 (as discussed during the first estimation phase), we now have reached a precision
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05d9911 and 551f9f6.

📒 Files selected for processing (16)
  • math/core/sources/internal/common.move (1 hunks)
  • math/core/sources/internal/macros.move (2 hunks)
  • math/core/sources/u128.move (1 hunks)
  • math/core/sources/u16.move (1 hunks)
  • math/core/sources/u256.move (1 hunks)
  • math/core/sources/u32.move (1 hunks)
  • math/core/sources/u64.move (1 hunks)
  • math/core/sources/u8.move (1 hunks)
  • math/core/tests/common_tests.move (1 hunks)
  • math/core/tests/macros_tests.move (1 hunks)
  • math/core/tests/u128_tests.move (1 hunks)
  • math/core/tests/u16_tests.move (1 hunks)
  • math/core/tests/u256_tests.move (1 hunks)
  • math/core/tests/u32_tests.move (1 hunks)
  • math/core/tests/u64_tests.move (1 hunks)
  • math/core/tests/u8_tests.move (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: ericnordelo
Repo: OpenZeppelin/contracts-sui PR: 40
File: math/core/sources/u8.move:50-57
Timestamp: 2025-11-04T14:38:12.309Z
Learning: In math/core/sources/{u8,u16,u32,u64,u128}.move files, the mul_shr implementations correctly ignore the overflow flag from macros::mul_shr! and rely on try_as_*() to detect when the result doesn't fit in the target type. Only u256.move needs to explicitly check the overflow flag because u256 operations can exceed u256 capacity.
<!-- [/add_learning]
📚 Learning: 2025-11-04T14:38:12.309Z
Learnt from: ericnordelo
Repo: OpenZeppelin/contracts-sui PR: 40
File: math/core/sources/u8.move:50-57
Timestamp: 2025-11-04T14:38:12.309Z
Learning: In math/core/sources/{u8,u16,u32,u64,u128}.move files, the mul_shr implementations correctly ignore the overflow flag from macros::mul_shr! and rely on try_as_*() to detect when the result doesn't fit in the target type. Only u256.move needs to explicitly check the overflow flag because u256 operations can exceed u256 capacity.
<!-- [/add_learning]

Applied to files:

  • math/core/sources/internal/common.move
  • math/core/sources/u16.move
  • math/core/tests/u16_tests.move
  • math/core/sources/u32.move
  • math/core/sources/u8.move
  • math/core/sources/u64.move
  • math/core/tests/common_tests.move
  • math/core/tests/u256_tests.move
  • math/core/tests/u8_tests.move
  • math/core/tests/macros_tests.move
  • math/core/sources/u256.move
  • math/core/tests/u64_tests.move
  • math/core/sources/u128.move
  • math/core/tests/u32_tests.move
  • math/core/tests/u128_tests.move
  • math/core/sources/internal/macros.move
📚 Learning: 2025-11-04T14:39:02.711Z
Learnt from: ericnordelo
Repo: OpenZeppelin/contracts-sui PR: 40
File: math/core/sources/u64.move:49-56
Timestamp: 2025-11-04T14:39:02.711Z
Learning: In mul_shr implementations for integer types smaller than u256 (u8, u16, u32, u64, u128), the overflow flag from macros::mul_shr! indicates u256 overflow, which is mathematically impossible for these smaller types. The pattern `let (_, result) = macros::mul_shr!(...); result.try_as_X()` is correct, as try_as_X() handles the conversion to Option<X> and returns None when the result doesn't fit in the target type. Only u256::mul_shr needs to explicitly check the overflow flag.

Applied to files:

  • math/core/tests/u256_tests.move
  • math/core/tests/macros_tests.move
  • math/core/sources/u256.move
  • math/core/sources/internal/macros.move
📚 Learning: 2025-11-04T14:38:12.309Z
Learnt from: ericnordelo
Repo: OpenZeppelin/contracts-sui PR: 40
File: math/core/sources/u8.move:50-57
Timestamp: 2025-11-04T14:38:12.309Z
Learning: In the OpenZeppelin Sui contracts math library, the `mul_shr!` and `mul_div!` macros return (overflow, result) tuples where overflow indicates the result cannot fit in u256. For integer types smaller than u256 (u8, u16, u32, u64, u128), the overflow flag can be safely ignored because the product of two values of these types will always fit in u256. The `try_as_*()` methods on the result handle the case where the u256 result doesn't fit in the target type by returning None.
<!-- [/add_learning]

Applied to files:

  • math/core/sources/u256.move
  • math/core/sources/internal/macros.move
📚 Learning: 2025-11-04T14:37:40.142Z
Learnt from: ericnordelo
Repo: OpenZeppelin/contracts-sui PR: 40
File: math/core/sources/u16.move:49-56
Timestamp: 2025-11-04T14:37:40.142Z
Learning: In the mul_shr! macro (and similar macros like mul_div!), the overflow flag indicates whether the result overflows u256, not the target type. For smaller types like u8, u16, u32, u64, and u128, the overflow flag will never be true because intermediate calculations always fit in u256. The try_as_uX() methods handle cases where the u256 result doesn't fit in the target type.

Applied to files:

  • math/core/sources/internal/macros.move
🔇 Additional comments (8)
math/core/tests/common_tests.move (1)

74-178: LGTM! Comprehensive test coverage for sqrt_floor.

The test suite thoroughly covers the sqrt_floor function across all scenarios:

  • Zero and edge cases
  • Perfect squares across multiple orders of magnitude
  • Non-perfect squares with floor rounding verification
  • All unsigned integer widths (u8–u256)
  • Maximum values for each width
math/core/tests/u128_tests.move (1)

462-566: LGTM! Thorough test coverage for u128::sqrt.

The test suite comprehensively validates u128::sqrt behavior:

  • All rounding modes (down, up, nearest) with multiple test cases
  • Perfect squares return exact results regardless of rounding
  • Edge cases including zero and max_value
  • Powers of four (perfect squares of powers of two)
  • Midpoint behavior for nearest-rounding

The tests follow the established pattern from log2/log256 tests and ensure correctness across the full range of u128.

math/core/sources/u8.move (1)

83-89: LGTM! Consistent API addition.

The sqrt function follows the established pattern in this module, delegating to the macro implementation and providing clear documentation. The signature and behavior are consistent with other mathematical functions (log2, log256).

math/core/sources/u256.move (1)

76-82: LGTM! Consistent API addition.

The sqrt function follows the established pattern in this module, with proper documentation and delegation to the macro implementation.

math/core/tests/u64_tests.move (1)

461-565: LGTM! Comprehensive test coverage for u64::sqrt.

The test suite thoroughly validates u64::sqrt behavior across all rounding modes, edge cases, and boundary values. The tests correctly verify that sqrt(u64::MAX) = 4294967295 for down-rounding and 4294967296 for up/nearest rounding.

math/core/tests/u8_tests.move (1)

409-523: LGTM! Excellent test coverage for u8::sqrt.

The test suite provides exceptional coverage for the u8 range, including all perfect squares from 1 to 225, comprehensive rounding mode tests, and correct max value handling. The tests ensure sqrt behaves correctly across the entire u8 domain.

math/core/sources/u16.move (1)

82-88: LGTM! Consistent API addition.

The sqrt function follows the established pattern across all width-specific modules, with proper documentation and delegation to the macro implementation. This completes the sqrt API surface across u8, u16, u32, u64, u128, and u256 types.

math/core/sources/internal/macros.move (1)

642-660: Nearest rounding guard is well derived.

Nice to see the half-up midpoint handled with the integer inequality value - floor² <= floor, which keeps the path branch-free and matches the tie-up behaviour we want.

@codecov
Copy link

codecov bot commented Nov 19, 2025

Codecov Report

❌ Patch coverage is 98.30508% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.23%. Comparing base (b548509) to head (cba2d38).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
math/core/sources/u128.move 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #53      +/-   ##
==========================================
+ Coverage   89.31%   90.23%   +0.92%     
==========================================
  Files          10       10              
  Lines         805      881      +76     
  Branches      272      292      +20     
==========================================
+ Hits          719      795      +76     
  Misses         71       71              
  Partials       15       15              
Flag Coverage Δ
math/core 90.23% <98.30%> (+0.92%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bidzyyys bidzyyys changed the title sqrt implementation and tests feat: sqrt implementation and tests Nov 19, 2025
Copy link
Collaborator

@bidzyyys bidzyyys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

Copy link
Member

@ericnordelo ericnordelo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job @immrsd.

Only comment, non-blocking and for your consideration (fill free to chose either way) is that maybe it makes sense to return the previous integer type on each public function, since it fits, and then people don't need to use try_as to convert if they need it.

@immrsd
Copy link
Collaborator Author

immrsd commented Nov 24, 2025

Only comment, non-blocking and for your consideration (fill free to chose either way) is that maybe it makes sense to return the previous integer type on each public function, since it fits, and then people don't need to use try_as to convert if they need it.

@ericnordelo I’ve considered that option but don’t think it’s feasible. The result of sqrt can still overflow a smaller uint type. For instance, sqrt(65_535) (max u16) is 255.99 and rounds to 256, which exceeds the maximum u8 value.

On top of that, I find it counter-intuitive to return a uint type different from the one used in the operation. In most cases sqrt will be part of a larger calculation that consistently operates on one specific uint type, so returning a different type would just force callers to cast the value back up anyway.

@immrsd immrsd merged commit 2ed3dec into main Nov 24, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: sqrt function

4 participants