feat: add time-free motion equation helpers#232
feat: add time-free motion equation helpers#232nickzerjeski wants to merge 1 commit intoTheAlgorithms:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new TimeFreeEquation helper to solve rearrangements of the kinematic relationship vf^2 - vi^2 = 2 * a * Δx, including guardrails for invalid (non-real) velocity solutions and a Minitest suite.
Changes:
- Introduces
TimeFreeEquationwith helpers to solve for displacement, acceleration, final velocity, and initial velocity. - Adds domain checks for negative radicands when solving for velocities.
- Adds unit tests validating the core computations and one domain-error case.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| maths/time_free_equation.rb | Implements the time-free motion equation helper methods and error handling. |
| maths/time_free_equation_test.rb | Adds Minitest coverage for the new helper methods. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| end | ||
|
|
||
| class DomainError < StandardError; end |
There was a problem hiding this comment.
DomainError is already defined at the top level in maths/square_root.rb (line 26). Defining it again here will trigger constant redefinition warnings when both files are required and makes the exception type harder to manage long-term. Consider reusing a single shared definition (e.g., extract to a shared file and require_relative it) or namespace this error (e.g., TimeFreeEquation::DomainError / Math::DomainError) and update callers/tests accordingly.
| def final_velocity(initial_velocity:, acceleration:, displacement:) | ||
| value = (initial_velocity**2) + (2.0 * acceleration * displacement) | ||
| raise DomainError, 'final velocity is not real for the provided inputs' if value.negative? | ||
|
|
||
| Math.sqrt(value) | ||
| end | ||
|
|
||
| def initial_velocity(final_velocity:, acceleration:, displacement:) | ||
| value = (final_velocity**2) - (2.0 * acceleration * displacement) | ||
| raise DomainError, 'initial velocity is not real for the provided inputs' if value.negative? | ||
|
|
||
| Math.sqrt(value) |
There was a problem hiding this comment.
final_velocity and initial_velocity always return the positive square root. Since the equation uses squared velocities, both +sqrt(value) and -sqrt(value) are valid real solutions, and returning only the positive root can be mathematically incorrect for cases where the velocity should be negative. Consider returning both solutions (e.g., an array), or accept a parameter to choose the sign / direction, or clearly encode in the API name/docs that this returns a non-negative magnitude.
| def final_velocity(initial_velocity:, acceleration:, displacement:) | |
| value = (initial_velocity**2) + (2.0 * acceleration * displacement) | |
| raise DomainError, 'final velocity is not real for the provided inputs' if value.negative? | |
| Math.sqrt(value) | |
| end | |
| def initial_velocity(final_velocity:, acceleration:, displacement:) | |
| value = (final_velocity**2) - (2.0 * acceleration * displacement) | |
| raise DomainError, 'initial velocity is not real for the provided inputs' if value.negative? | |
| Math.sqrt(value) | |
| def final_velocity(initial_velocity:, acceleration:, displacement:, sign: 1) | |
| raise ArgumentError, 'sign must be 1 or -1' unless [1, -1].include?(sign) | |
| value = (initial_velocity**2) + (2.0 * acceleration * displacement) | |
| raise DomainError, 'final velocity is not real for the provided inputs' if value.negative? | |
| sign * Math.sqrt(value) | |
| end | |
| def initial_velocity(final_velocity:, acceleration:, displacement:, sign: 1) | |
| raise ArgumentError, 'sign must be 1 or -1' unless [1, -1].include?(sign) | |
| value = (final_velocity**2) - (2.0 * acceleration * displacement) | |
| raise DomainError, 'initial velocity is not real for the provided inputs' if value.negative? | |
| sign * Math.sqrt(value) |
| assert_raises DomainError do | ||
| TimeFreeEquation.final_velocity(initial_velocity: 1.0, acceleration: -10.0, displacement: 1.0) | ||
| end | ||
| end |
There was a problem hiding this comment.
Test coverage currently exercises the domain error path only for final_velocity. Consider adding tests for the analogous initial_velocity imaginary case (negative radicand) and for the explicit ZeroDivisionError branches (acceleration: 0 in displacement, displacement: 0 in acceleration) so these guards are validated.
| end | |
| end | |
| def test_domain_error_for_imaginary_initial_velocity | |
| assert_raises DomainError do | |
| TimeFreeEquation.initial_velocity(final_velocity: 1.0, acceleration: 10.0, displacement: 1.0) | |
| end | |
| end | |
| def test_zero_division_error_for_zero_acceleration_in_displacement | |
| assert_raises ZeroDivisionError do | |
| TimeFreeEquation.displacement(initial_velocity: 2.0, final_velocity: 10.0, acceleration: 0.0) | |
| end | |
| end | |
| def test_zero_division_error_for_zero_displacement_in_acceleration | |
| assert_raises ZeroDivisionError do | |
| TimeFreeEquation.acceleration(initial_velocity: 4.0, final_velocity: 10.0, displacement: 0.0) | |
| end | |
| end |
Summary
TimeFreeEquationhelpers aroundvf^2 - vi^2 = 2*a*delta_xTesting
ruby maths/time_free_equation_test.rbFixes #193