zold-io/zold

View on GitHub
lib/zold/amount.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

# Copyright (c) 2018-2023 Zerocracy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'rainbow'

# The amount.
# Author:: Yegor Bugayenko (yegor256@gmail.com)
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
# License:: MIT
module Zold
  # Amount
  class Amount
    # Maximum amount of zents
    MAX = 2**63

    # How many zents are in one ZLD: 2^FRACTION
    FRACTION = 32
    private_constant :FRACTION

    def initialize(zents: nil, zld: nil)
      if !zents.nil?
        raise "Integer is required, while #{zents.class} provided: #{zents}" unless zents.is_a?(Integer)
        @zents = zents
      elsif !zld.nil?
        raise "Float is required, while #{zld.class} provided: #{zld}" unless zld.is_a?(Float)
        @zents = (zld * (2**FRACTION)).to_i
      else
        raise 'You can\'t specify both coints and zld'
      end
      raise "The amount is too big: #{@zents}" if @zents > MAX
      raise "The amount is too small: #{@zents}" if @zents < -MAX
    end

    # Just zero, for convenience.
    ZERO = Amount.new(zents: 0)

    # Convert it to zents and return as an integer.
    def to_i
      @zents
    end

    # Convert to ZLD and return as a float.
    def to_f
      @zents.to_f / (2**FRACTION)
    end

    # Convert to ZLD and return as a string. If you need float, you should use <tt>to_f()</tt> later.
    def to_zld(digits = 2)
      format("%0.#{digits}f", to_f)
    end

    def to_s
      text = "#{to_zld}ZLD"
      if positive?
        Rainbow(text).green
      elsif negative?
        Rainbow(text).red
      else
        text
      end
    end

    def ==(other)
      raise "== may only work with Amount: #{other}" unless other.is_a?(Amount)
      @zents == other.to_i
    end

    def >(other)
      raise '> may only work with Amount' unless other.is_a?(Amount)
      @zents > other.to_i
    end

    def <(other)
      raise '< may only work with Amount' unless other.is_a?(Amount)
      @zents < other.to_i
    end

    def <=(other)
      raise '<= may only work with Amount' unless other.is_a?(Amount)
      @zents <= other.to_i
    end

    def <=>(other)
      raise '<= may only work with Amount' unless other.is_a?(Amount)
      @zents <=> other.to_i
    end

    def +(other)
      raise '+ may only work with Amount' unless other.is_a?(Amount)
      Amount.new(zents: @zents + other.to_i)
    end

    def -(other)
      raise '- may only work with Amount' unless other.is_a?(Amount)
      Amount.new(zents: @zents - other.to_i)
    end

    def zero?
      @zents.zero?
    end

    def negative?
      @zents.negative?
    end

    def positive?
      @zents.positive?
    end

    def *(other)
      raise '* may only work with a number' unless other.is_a?(Integer) || other.is_a?(Float)
      c = (@zents * other).to_i
      raise "Overflow, can't multiply #{@zents} by #{m}" if c > MAX
      Amount.new(zents: c)
    end

    def /(other)
      raise '/ may only work with a number' unless other.is_a?(Integer) || other.is_a?(Float)
      Amount.new(zents: (@zents / other).to_i)
    end
  end
end