Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions maths/time_free_equation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

# Solves forms of the kinematic equation:
# vf^2 - vi^2 = 2 * a * delta_x
class TimeFreeEquation
class << self
def displacement(initial_velocity:, final_velocity:, acceleration:)
raise ZeroDivisionError, 'acceleration cannot be zero' if acceleration.zero?

((final_velocity**2) - (initial_velocity**2)) / (2.0 * acceleration)
end

def acceleration(initial_velocity:, final_velocity:, displacement:)
raise ZeroDivisionError, 'displacement cannot be zero' if displacement.zero?

((final_velocity**2) - (initial_velocity**2)) / (2.0 * displacement)
end

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)
Comment on lines +19 to +30
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
end
end
end

class DomainError < StandardError; end
36 changes: 36 additions & 0 deletions maths/time_free_equation_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require 'minitest/autorun'
require_relative './time_free_equation'

class TimeFreeEquationTest < Minitest::Test
def test_displacement
assert_in_delta 24.0,
TimeFreeEquation.displacement(initial_velocity: 2.0, final_velocity: 10.0, acceleration: 2.0),
1E-12
end

def test_acceleration
assert_in_delta 3.0,
TimeFreeEquation.acceleration(initial_velocity: 4.0, final_velocity: 10.0, displacement: 14.0),
1E-12
end

def test_final_velocity
assert_in_delta 13.0,
TimeFreeEquation.final_velocity(initial_velocity: 5.0, acceleration: 3.0, displacement: 24.0),
1E-12
end

def test_initial_velocity
assert_in_delta 6.0,
TimeFreeEquation.initial_velocity(final_velocity: 14.0, acceleration: 4.0, displacement: 20.0),
1E-12
end

def test_domain_error_for_imaginary_velocity
assert_raises DomainError do
TimeFreeEquation.final_velocity(initial_velocity: 1.0, acceleration: -10.0, displacement: 1.0)
end
end
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
end
Loading