Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

num/wrap_around.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature wrap_around
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# wrap_around -- abstract ancestor of wrap-around integer numbers
#
# wrap_around is the abstract ancestor of integer numbers that have min and
# max values and operations with wrap-around semantics.
#
module:public wrap_around : integer is

  # overflow checking

  # would negation -wrap_around.this cause an overflow?
  public wrapped_on_neg bool => abstract

  # would addition wrap_around.this + other cause an overflow or underflow?
  public overflow_on_add(other wrap_around.this) bool => abstract
  public underflow_on_add(other wrap_around.this) bool => abstract
  public wrapped_on_add(other wrap_around.this) => overflow_on_add other || underflow_on_add other

  # would subtraction wrap_around.this - other cause an overflow or underflow?
  public overflow_on_sub(other wrap_around.this) bool => abstract
  public underflow_on_sub(other wrap_around.this) bool => abstract
  public wrapped_on_sub(other wrap_around.this) => overflow_on_sub other || underflow_on_sub other

  # would multiplication wrap_around.this * other cause an overflow or underflow?
  public overflow_on_mul(other wrap_around.this) bool => abstract
  public underflow_on_mul(other wrap_around.this) bool => abstract
  public wrapped_on_mul(other wrap_around.this) => overflow_on_mul other || underflow_on_mul other

  # preconditions used in 'numeric' for basic operations: true if the
  # operation is permitted for the given values
  public prefix -! => !wrapped_on_neg
  public infix +! (other wrap_around.this) => !(overflow_on_add other) && !(underflow_on_add other)
  public infix -! (other wrap_around.this) => !(overflow_on_sub other) && !(underflow_on_sub other)
  public infix *! (other wrap_around.this) => !(overflow_on_mul other) && !(underflow_on_mul other)
  public infix /! (other wrap_around.this) => true  # other != zero is checked in separate condition
  public infix %! (other wrap_around.this) => true  # other != zero is checked in separate condition
  public infix **!(other wrap_around.this) => !(overflow_on_exp other)

  # neg, add, sub, mul with wrap-around semantics
  public prefix -° wrap_around.this => abstract
  public infix +° (other wrap_around.this) wrap_around.this => abstract
  public infix -° (other wrap_around.this) wrap_around.this => abstract
  public infix *° (other wrap_around.this) wrap_around.this => abstract

  public is_min => wrap_around.this = wrap_around.this.min
  public is_max => wrap_around.this = wrap_around.this.max

  # check if this type of wrap_around is bounded
  #
  # wrap_arounds are assumed to be a bound set by default, so
  # this returns true unless redefined by an implementation
  public redef is_bounded => true

  # basic operations with runtime error on overflow

  # negation, with check for overflow
  public redef prefix - wrap_around.this
    pre
      debug: !wrapped_on_neg
  => -° wrap_around.this

  # addition, with check for overflow
  public redef infix +  (other wrap_around.this) wrap_around.this
    pre
      debug: !(overflow_on_add other)
      debug: !(underflow_on_add other)
  => wrap_around.this +° other

  # subtraction, with check for overflow
  public redef infix -  (other wrap_around.this) wrap_around.this
    pre
      debug: !(overflow_on_sub other)
      debug: !(underflow_on_sub other)
  => wrap_around.this -° other

  # multiplication, with check for overflow
  public redef infix *  (other wrap_around.this) wrap_around.this
    pre
      debug: !(overflow_on_mul other)
      debug: !(underflow_on_mul other)
  => wrap_around.this *° other

  # overflow checking operations
  public redef prefix -? num_option wrap_around.this => if wrapped_on_neg then nil else -wrap_around.this
  public redef infix +? (other wrap_around.this) num_option wrap_around.this => if wrapped_on_add other then nil else wrap_around.this + other
  public redef infix -? (other wrap_around.this) num_option wrap_around.this => if wrapped_on_sub other then nil else wrap_around.this - other
  public redef infix *? (other wrap_around.this) num_option wrap_around.this => if wrapped_on_mul other then nil else wrap_around.this * other


  # saturating  operations
  public redef prefix -^ => if wrapped_on_neg if wrap_around.this > wrap_around.this.zero then wrap_around.this.min else wrap_around.this.max else - wrap_around.this
  public redef infix +^ (other wrap_around.this) => if overflow_on_add other then wrap_around.this.max else if underflow_on_add other then wrap_around.this.min else wrap_around.this + other
  public redef infix -^ (other wrap_around.this) => if overflow_on_sub other then wrap_around.this.max else if underflow_on_sub other then wrap_around.this.min else wrap_around.this - other
  public redef infix *^ (other wrap_around.this) => if overflow_on_mul other then wrap_around.this.max else if underflow_on_mul other then wrap_around.this.min else wrap_around.this * other


  # exponentiation for positive exponent
  #
  # 'zero ** zero' is permitted and results in 'one'.
  #
  public infix ** (other wrap_around.this) wrap_around.this
    pre
      safety: (other ≥ wrap_around.this.zero)
      debug: (!overflow_on_exp other)
  =>
    wrap_around.this **° other


  # exponentiation with wrap-around semantics
  #
  # 'zero **° zero' is permitted and results in 'one'.
  #
  public infix **° (other wrap_around.this) wrap_around.this
    pre
      safety: (other ≥ wrap_around.this.zero)
  =>
    if      (other = wrap_around.this.zero) wrap_around.this.one
    else if (other = wrap_around.this.one ) wrap_around.this
    else
      tmp := (wrap_around.this *° wrap_around.this) **° (other / wrap_around.this.two)
      if other %% wrap_around.this.two
        tmp
      else
        tmp *° wrap_around.this


  # exponentiation with overflow checking semantics
  #
  # 'zero **? zero' is permitted and results in 'one'.
  #
  public infix **? (other wrap_around.this) num_option wrap_around.this
    pre
      safety: (other ≥ wrap_around.this.zero)
  =>
    if      (other = wrap_around.this.zero) wrap_around.this.one
    else if (other = wrap_around.this.one ) wrap_around.this
    else
      tmp := (wrap_around.this *? wrap_around.this) **? (other / wrap_around.this.two)
      if other %% wrap_around.this.two
        tmp
      else
        tmp *? wrap_around.this


  # would exponentiation 'this ** other' cause an overflow?
  #
  public overflow_on_exp(other wrap_around.this) => (wrap_around.this **? other)!!


  # exponentiation with saturating semantics
  #
  # 'zero **^ zero' is permitted and results in 'one'.
  #
  public infix **^ (other wrap_around.this) wrap_around.this
    pre
      safety: (other ≥ wrap_around.this.zero)
  =>
    if overflow_on_exp other
      if (wrap_around.this ≥ wrap_around.this.zero) || (other %% wrap_around.this.two)
        wrap_around.this.max
      else
        wrap_around.this.min
    else
      wrap_around.this ** other

  # bitwise NOT
  public redef prefix ~ wrap_around.this
    pre
      is_bounded
  =>
    wrap_around.this ^ wrap_around.this.all_bits_set


  # count the number of 1 bits in the binary representation of this
  # integer.
  #
  public ones_count i32 => abstract


  # this integer as an array of bytes (little endian)
  public as_bytes array u8 =>
    bs := wrap_around.this.byte_size
    array u8 bs (idx ->
      shift wrap_around.this := wrap_around.this.from_u32 (bs.as_u32-idx.as_u32-1)*8
      (wrap_around.this>>shift).low8bits)



  # returns the number in whose bit representation all bits are ones
  public type.all_bits_set wrap_around.this => abstract


  # how many bytes does this integer use?
  public type.byte_size i32 =>
    # NYI #1179
    all_bits_set.ones_count / 8


  # minimum
  #
  public type.min wrap_around.this => abstract


  # maximum
  #
  public type.max wrap_around.this => abstract