class Fractify::Fraction

Attributes

denominator[RW]
negative[RW]
numerator[RW]
whole_part[RW]

Public Class Methods

floating_point_part(number) click to toggle source
# File lib/fractify/fraction.rb, line 255
def self.floating_point_part(number)
  raise IncorrectArgumentsError unless number.is_a? Numeric

  result = '0.'
  number_string = number.to_s
  past_decimal_separator = false

  number_string.each_char do |c|
    if past_decimal_separator
      result += c
    elsif c == '.'
      past_decimal_separator = true
    end
  end

  result.to_f
end
greatest_common_divisor(first, second) click to toggle source
# File lib/fractify/fraction.rb, line 279
def self.greatest_common_divisor(first, second)
  return first if second.zero?

  greatest_common_divisor(second, first % second)
end
least_common_multiple(first, second) click to toggle source
# File lib/fractify/fraction.rb, line 273
def self.least_common_multiple(first, second)
  raise IncorrectArgumentsError unless first.is_a?(Numeric) && second.is_a?(Numeric)

  (first * second) / greatest_common_divisor(first, second)
end
letter?(char) click to toggle source
# File lib/fractify/fraction.rb, line 289
def self.letter?(char)
  char =~ /[[:alpha:]]/
end
new(whole_part: 0, numerator: 0, denominator: 1, string: '', numeric: false) click to toggle source
# File lib/fractify/fraction.rb, line 10
def initialize(whole_part: 0, numerator: 0, denominator: 1, string: '', numeric: false)
  unless string.strip.empty?
    to_zero!
    parse_fraction_string!(string)
    return
  end

  if numeric.is_a? Numeric
    convert_number!(numeric)
    return
  end
  raise DividingByZeroError if denominator.zero? && numerator != 0

  self.whole_part = whole_part
  self.numerator = numerator
  self.denominator = denominator
  self.negative = false

  fix_negativity!
  simplify!
  to_improper!
end
numeric?(char) click to toggle source
# File lib/fractify/fraction.rb, line 285
def self.numeric?(char)
  char =~ /[[:digit:]]/
end
valid?(string) click to toggle source
# File lib/fractify/fraction.rb, line 249
def self.valid?(string)
  return false unless string.is_a? String

  validate_fraction_string(string)
end
validate_float_string(string) click to toggle source
# File lib/fractify/fraction.rb, line 386
def self.validate_float_string(string)
  open_bracket, incorrect_syntax, at_end, digit_present,
  floating_point_present = false
  minus_is_free = true

  string.each_char do |c|
    if at_end || letter?(c)
      incorrect_syntax = true
      break
    end

    if open_bracket
      if c == '-'
        if !minus_is_free || digit_present
          incorrect_syntax = true
          break
        end
        minus_is_free = false
      elsif numeric?(c)
        digit_present = true
      elsif c == '.'
        if floating_point_present || !digit_present
          incorrect_syntax = true
          break
        end
        floating_point_present = true
      elsif c == ')'
        open_bracket = false
        at_end = true
      else
        incorrect_syntax = true
        break
      end
    elsif c == '('
      open_bracket = true
    else
      incorrect_syntax = true
      break
    end
  end

  incorrect_syntax = true if open_bracket

  !incorrect_syntax
end
validate_fraction_string(string) click to toggle source
# File lib/fractify/fraction.rb, line 293
def self.validate_fraction_string(string)
  is_float = false
  string.each_char do |c|
    if c == '.'
      is_float = true
      break
    end
  end

  if is_float
    validate_float_string(string)
  else
    validate_string(string)
  end
end
validate_string(string) click to toggle source
# File lib/fractify/fraction.rb, line 309
def self.validate_string(string)
  stage = 0
  open_bracket, incorrect_syntax, at_end, digit_present = false
  minus_is_free = true

  string.each_char do |c|
    if at_end || letter?(c)
      incorrect_syntax = true
      break
    end

    if open_bracket
      if stage.zero?
        if c == '-' && minus_is_free
          minus_is_free = false
        elsif numeric?(c)
          digit_present = true
        elsif c == ' ' && digit_present
          stage = 1
          minus_is_free = true
          digit_present = false
        elsif c == '/' && digit_present
          stage = 2
          minus_is_free = true
          digit_present = false
        elsif c == ')' && digit_present
        else
          incorrect_syntax = true
          break
        end
      elsif stage == 1
        if c == '-' && minus_is_free
          minus_is_free = false
        elsif numeric?(c)
          digit_present = true
        elsif c == '/' && digit_present
          stage = 2
          minus_is_free = true
          digit_present
        else
          incorrect_syntax = true
          break
        end
      elsif stage == 2
        if c == '-' && minus_is_free
          minus_is_free = false
        elsif numeric?(c)
          digit_present = true
        elsif c == ')' && digit_present
        else
          incorrect_syntax = true
          break
        end
      end
    end

    if c == '('
      if open_bracket
        incorrect_syntax = true
        break
      end
      open_bracket = true
    elsif c == ')'
      unless open_bracket
        incorrect_syntax = true
        break
      end
      open_bracket = false
      at_end = true
    end
  end

  incorrect_syntax = true if open_bracket

  !incorrect_syntax
end

Public Instance Methods

*(other) click to toggle source
# File lib/fractify/fraction.rb, line 194
def *(other)
  check_argument(other)

  fraction = dup
  object = if other.is_a? Numeric
             Fractify::Fraction.new(numeric: other)
           else
             other.dup
           end

  fraction.to_improper!
  object.to_improper!

  negative_counter = 0
  negative_counter += 1 if fraction.negative?
  negative_counter += 1 if object.negative?
  fraction.negative = negative_counter.even? ? false : true

  fraction.numerator *= object.numerator
  fraction.denominator *= object.denominator

  fraction.simplify!

  fraction.whole_part = 1 if fraction.zero?

  fraction
end
**(other) click to toggle source
# File lib/fractify/fraction.rb, line 239
def **(other)
  check_argument(other)
  if other.is_a? Numeric
    power(other)
  else
    x = other.to_f
    power(x)
  end
end
+(other) click to toggle source
# File lib/fractify/fraction.rb, line 173
def +(other)
  check_argument(other)

  fraction = dup
  object = if other.is_a? Numeric
             Fractify::Fraction.new(numeric: other)
           else
             other.dup
           end
  fraction.to_common_denominator!(object) unless object.zero?

  fraction.minus_to_numerator!
  object.minus_to_numerator!

  fraction.numerator += object.numerator
  fraction.minus_to_negative_field!
  fraction.simplify!

  fraction
end
-(other) click to toggle source
# File lib/fractify/fraction.rb, line 152
def -(other)
  check_argument(other)

  fraction = dup
  object = if other.is_a? Numeric
             Fractify::Fraction.new(numeric: other)
           else
             other.dup
           end
  fraction.to_common_denominator!(object) unless object.zero?

  fraction.minus_to_numerator!
  object.minus_to_numerator!

  fraction.numerator -= object.numerator
  fraction.minus_to_negative_field!
  fraction.simplify!

  fraction
end
/(other) click to toggle source
# File lib/fractify/fraction.rb, line 222
def /(other)
  check_argument(other)
  raise DividingByZeroError if other.zero?

  fraction = dup
  object = if other.is_a? Numeric
             Fractify::Fraction.new(numeric: other)
           else
             other.dup
           end

  object.to_reciprocal!
  fraction *= object

  fraction
end
integer?() click to toggle source
# File lib/fractify/fraction.rb, line 43
def integer?
  return true if denominator == 1 && !zero?

  false
end
negative?() click to toggle source
# File lib/fractify/fraction.rb, line 33
def negative?
  negative
end
one?() click to toggle source
# File lib/fractify/fraction.rb, line 49
def one?
  return true if numerator == 1 && denominator == 1 && whole_part.zero?

  false
end
puts() click to toggle source
# File lib/fractify/fraction.rb, line 97
def puts
  puts to_s
end
simplify!() click to toggle source
# File lib/fractify/fraction.rb, line 55
def simplify!
  return true if integer?

  if zero?
    @denominator = 1
    @whole_part = 0
    @negative = false
  else
    greatest_common_divisor = self.class.greatest_common_divisor(numerator, denominator)

    @denominator /= greatest_common_divisor
    @numerator /= greatest_common_divisor
  end

  true
end
to_common_denominator!(other) click to toggle source
# File lib/fractify/fraction.rb, line 432
def to_common_denominator!(other)
  to_improper!
  other.to_improper!

  least_common_multiple = self.class.least_common_multiple(denominator, other.denominator)

  numerator_multiplication = least_common_multiple / denominator
  @numerator *= numerator_multiplication
  @denominator = least_common_multiple

  numerator_multiplication = least_common_multiple / other.denominator
  other.numerator *= numerator_multiplication
  other.denominator = least_common_multiple

  true
end
to_f() click to toggle source
# File lib/fractify/fraction.rb, line 110
def to_f
  return 0.0 if zero?

  fraction = dup
  fraction.to_improper!
  fraction.minus_to_numerator!

  fraction.numerator.to_f / fraction.denominator
end
to_i() click to toggle source
# File lib/fractify/fraction.rb, line 120
def to_i
  return 0 if zero?
  fraction = dup
  fraction.to_proper_fraction!

  result = fraction.whole_part
  result = -result if fraction.negative?

  result
end
to_improper!() click to toggle source
# File lib/fractify/fraction.rb, line 87
def to_improper!
  return true if whole_part.zero?

  @numerator -= 1 if numerator == 1 && denominator == 1
  @numerator += whole_part * denominator
  @whole_part = 0

  true
end
to_proper_fraction!() click to toggle source
# File lib/fractify/fraction.rb, line 72
def to_proper_fraction!
  return true if zero?

  if denominator == 1 && numerator != 1
    @whole_part += numerator
    @numerator = 1
  elsif numerator > denominator
    new_whole_part = numerator / denominator
    @whole_part += new_whole_part
    @numerator %= denominator
  end

  true
end
to_reciprocal!() click to toggle source
# File lib/fractify/fraction.rb, line 449
def to_reciprocal!
  to_improper!
  aux = denominator
  @denominator = numerator
  @numerator = aux

  true
end
to_s(improper = false) click to toggle source
# File lib/fractify/fraction.rb, line 131
def to_s(improper = false)
  fraction = dup
  fraction.to_proper_fraction! unless improper
  fraction_string = '('
  fraction_string += '-' if negative?

  if fraction.zero?
    fraction_string += '0'
  elsif fraction.one?
    fraction_string += '1'
  else
    fraction_string += fraction.whole_part.to_s if fraction.whole_part != 0
    if fraction.numerator != 1 || fraction.denominator != 1
      fraction_string += ' ' if fraction.whole_part != 0
      fraction_string += fraction.numerator.to_s + '/' + fraction.denominator.to_s
    end
  end

  fraction_string + ')'
end
to_zero!() click to toggle source
# File lib/fractify/fraction.rb, line 101
def to_zero!
  @whole_part = 0
  @numerator = 0
  @denominator = 1
  @negative = false

  true
end
zero?() click to toggle source
# File lib/fractify/fraction.rb, line 37
def zero?
  return true if numerator.zero?

  false
end

Protected Instance Methods

minus_to_negative_field!() click to toggle source
# File lib/fractify/fraction.rb, line 469
def minus_to_negative_field!
  return true unless numerator.negative?

  @negative = true
  @numerator = numerator.abs

  true
end
minus_to_numerator!() click to toggle source
# File lib/fractify/fraction.rb, line 460
def minus_to_numerator!
  return true unless negative

  @negative = false
  @numerator = -numerator

  true
end

Private Instance Methods

arguments_make_negative?() click to toggle source
# File lib/fractify/fraction.rb, line 521
def arguments_make_negative?
  negative_attributes = 0
  %w[whole_part numerator denominator].each do |a|
    negative_attributes += 1 if send(a).negative?
  end
  return true if negative_attributes.odd?

  false
end
check_argument(other) click to toggle source
# File lib/fractify/fraction.rb, line 480
def check_argument(other)
  return unless !other.is_a?(Numeric) && !other.is_a?(Fractify::Fraction)

  raise IncorrectArgumentsError
end
convert_number!(number) click to toggle source
# File lib/fractify/fraction.rb, line 693
def convert_number!(number)
  if number.zero?
    to_zero!
    return
  end

  if number.negative?
    number = number.abs
    @negative = true
  end

  @whole_part = number.to_i
  new_denominator = 1
  floating_point_part = self.class.floating_point_part(number)

  while floating_point_part.positive?
    floating_point_part *= new_denominator
    floating_point_part = self.class.floating_point_part(floating_point_part)
    new_denominator *= 10
  end

  @numerator = self.class.floating_point_part(number) * new_denominator
  @numerator = numerator.to_i
  @denominator = new_denominator

  @numerator = 1 if numerator.zero?

  simplify!
  to_improper!
end
fix_negativity!() click to toggle source
# File lib/fractify/fraction.rb, line 511
def fix_negativity!
  @negative = !negative if arguments_make_negative?

  @whole_part = whole_part.abs
  @numerator = numerator.abs
  @denominator = denominator.abs

  true
end
parse_float_string!(string) click to toggle source
# File lib/fractify/fraction.rb, line 642
def parse_float_string!(string)
  number_string = ''
  open_bracket, incorrect_syntax, at_end, digit_present,
  floating_point_present = false

  minus_is_free = true

  string.each_char do |c|
    if at_end || self.class.letter?(c)
      incorrect_syntax = true
      break
    end

    if open_bracket
      if c == '-'
        if !minus_is_free || digit_present
          incorrect_syntax = true
          break
        end
        minus_is_free = false
        number_string += c
      elsif self.class.numeric?(c)
        digit_present = true
        number_string += c
      elsif c == '.'
        if floating_point_present || !digit_present
          incorrect_syntax = true
          break
        end
        floating_point_present = true
        number_string += c
      elsif c == ')'
        open_bracket = false
        at_end = true
      else
        incorrect_syntax = true
        break
      end
    elsif c == '('
      open_bracket = true
    else
      incorrect_syntax = true
      break
    end
  end

  raise IncorrectStringSyntax if open_bracket || incorrect_syntax

  convert_number!(number_string.to_f)
end
parse_fraction_string!(string) click to toggle source
# File lib/fractify/fraction.rb, line 531
def parse_fraction_string!(string)
  is_float = false
  string.each_char do |c|
    if c == '.'
      is_float = true
      break
    end
  end

  if is_float
    parse_float_string!(string)
  else
    parse_string!(string)
  end
end
parse_string!(string) click to toggle source
# File lib/fractify/fraction.rb, line 547
def parse_string!(string)
  stage = 0
  number_string = ''

  open_bracket, incorrect_syntax, at_end, digit_present = false

  minus_is_free = true

  string.each_char do |c|
    if at_end || self.class.letter?(c)
      incorrect_syntax = true
      break
    end

    if open_bracket
      if stage.zero?
        if c == '-' && minus_is_free
          @negative = true
          minus_is_free = false
        elsif self.class.numeric?(c)
          number_string += c
          digit_present = true
        elsif c == ' ' && digit_present
          stage = 1
          @whole_part = number_string.to_i
          number_string = ''
          minus_is_free = true
          digit_present = false
        elsif c == '/' && digit_present
          stage = 2
          @numerator = number_string.to_i
          number_string = ''
          minus_is_free = true
          digit_present = false
        elsif c == ')' && digit_present
          @numerator = number_string.to_i
          @denominator = 1
        else
          incorrect_syntax = true
          break
        end
      elsif stage == 1
        if c == '-' && minus_is_free
          @negative = !negative
          minus_is_free = false
        elsif self.class.numeric?(c)
          number_string += c
          digit_present = true
        elsif c == '/' && digit_present
          stage = 2
          @numerator = number_string.to_i
          number_string = ''
          minus_is_free = true
          digit_present
        else
          incorrect_syntax = true
          break
        end
      elsif stage == 2
        if c == '-' && minus_is_free
          @negative = !negative
          minus_is_free = false
        elsif self.class.numeric?(c)
          number_string += c
          digit_present = true
        elsif c == ')' && digit_present
          @denominator = number_string.to_i
        else
          incorrect_syntax = true
          break
        end
      end
    end

    if c == '('
      if open_bracket
        incorrect_syntax = true
        break
      end
      open_bracket = true
    elsif c == ')'
      unless open_bracket
        incorrect_syntax = true
        break
      end
      open_bracket = false
      at_end = true
    end
  end

  raise IncorrectStringSyntax if incorrect_syntax || open_bracket

  simplify!
end
power(x) click to toggle source
# File lib/fractify/fraction.rb, line 486
def power(x)
  fraction = dup
  fraction.negative = false if (x % 2).zero?

  if x.negative?
    fraction.to_reciprocal!
    x = x.abs
  else
    fraction.to_improper!
  end

  if x == x.to_i
    fraction.numerator **= x.to_i
    fraction.denominator **= x.to_i
    fraction.simplify!
  else
    float = fraction.to_f.abs
    float **= x
    float = -float if fraction.negative?
    fraction = Fractify::Fraction.new(numeric: float)
  end

  fraction
end