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

wrappingInteger.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 wrappingInteger
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# wrappingInteger
#
# wrappingInteger is the abstract ancestor of integer numbers that have min and
# max values and operations with wrap-around semantics.
#
wrappingInteger<T: wrappingInteger<T>> : integer<T>, wrappingIntegers<T> is

  # overflow checking

  # would negation -thiz cause an overflow?
  wrappedOnNeg bool is abstract

  # would addtion thiz + other cause an overflow or underflow?
  overflowOnAdd(other T) bool is abstract
  underflowOnAdd(other T) bool is abstract
  wrappedOnAdd(other T) => overflowOnAdd(other) || underflowOnAdd(other)

  # would subtraction thiz - other cause an overflow or underflow?
  overflowOnSub(other T) bool is abstract
  underflowOnSub(other T) bool is abstract
  wrappedOnSub(other T) => overflowOnSub(other) || underflowOnSub(other)

  # would multiplication thiz * other cause an overflow or underflow?
  overflowOnMul(other T) bool is abstract
  underflowOnMul(other T) bool is abstract
  wrappedOnMul(other T) => overflowOnMul(other) || underflowOnMul(other)

  # preconditions used in 'numeric' for basic operations: true if the
  # operation is permitted for the given values
  prefix -! => !wrappedOnNeg
  infix +! (other T) => !(overflowOnAdd other) && !(underflowOnAdd other)
  infix -! (other T) => !(overflowOnSub other) && !(underflowOnSub other)
  infix *! (other T) => !(overflowOnMul other) && !(underflowOnMul other)
  infix /! (other T) => true  # other != zero is checked in separate condition
  infix %! (other T) => true  # other != zero is checked in separate condition
  infix **!(other T) => !(overflowOnExp other)

  # neg, add, sub, mul with wrap-around semantics
  prefix -° T is abstract
  infix +° (other T) T is abstract
  infix -° (other T) T is abstract
  infix *° (other T) T is abstract

  isMin => thiz == min
  isMax => thiz == max

  # basic operations with runtime error on overflow

  # negation, with check for overflow
  redef prefix - T
    pre
      debug: !wrappedOnNeg
  is -° thiz

  # addition, with check for overflow
  redef infix +  (other T) T
    pre
      debug: !overflowOnAdd(other),
      debug: !underflowOnAdd(other)
  is thiz +° other

  # substraction, with check for overflow
  redef infix -  (other T) T
    pre
      debug: !overflowOnSub(other),
      debug: !underflowOnSub(other)
  is thiz -° other

  # multiplication, with check for overflow
  redef infix *  (other T) T
    pre
      debug: !overflowOnMul(other),
      debug: !underflowOnMul(other)
  is thiz *° other

  # overflow checking operations
  redef prefix -?          numOption<T> is if wrappedOnNeg        then nil else -thiz
  redef infix +? (other T) numOption<T> is if wrappedOnAdd(other) then nil else thiz + other
  redef infix -? (other T) numOption<T> is if wrappedOnSub(other) then nil else thiz - other
  redef infix *? (other T) numOption<T> is if wrappedOnMul(other) then nil else thiz * other

  # saturating  operations
  redef prefix -^          => if wrappedOnNeg if thiz > zero then min else max else - thiz
  redef infix +^ (other T) => if overflowOnAdd(other) then max else if underflowOnAdd(other) then min else thiz + other
  redef infix -^ (other T) => if overflowOnSub(other) then max else if underflowOnSub(other) then min else thiz - other
  redef infix *^ (other T) => if overflowOnMul(other) then max else if underflowOnMul(other) then min else thiz * other


  # exponentiation for positive exponent
  #
  # 'zero ** zero' is permitted and results in 'one'.
  #
  infix ** (other T) T
    pre
      safety: other >= zero,
      debug: !overflowOnExp(other)
  is
    thiz **° other


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


  # exponentiation with overflow checking semantics
  #
  # 'zero **? zero' is permitted and results in 'one'.
  #
  infix **? (other T) numOption<T>
    pre
      safety: other >= zero
  is
    if      (other = zero) one
    else if (other = one ) thiz
    else
      tmp := (thiz *? thiz) **? (other / two)
      if other %% two
        tmp
      else
        tmp *? thiz


  # would exponentiation 'this ** other' cause an overflow?
  #
  overflowOnExp(other T) => (thiz **? other)!!


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


# wrappingIntegers -- unit type defining features related to wrappingInteger
# but not requiring an instance
#
wrappingIntegers<T: wrappingInteger<T>> : numerics<T> is

  min T is abstract
  max T is abstract