{ debug, expression, float, integer, intrinsics, list, string, type, ... }:
  let
    assertNumber#: int | float -> int | float | !
    =   value:
          matchNumber value
          {
            float                       =   value;
            int                         =   value;
          };

    matchNumber#: T -> { int: R, float: R } -> R | !
    # where T, R: Any
    =   value:
        { int, float } @ select:
          type.matchPrimitiveOrDefault
            value
            select
            ( debug.panic "matchNumber" "Value is not a number: Neither int nor float!" );

    abs#: int | float -> int | float
    =   value:
          let
            value'                      =   assertNumber value;
          in
            if value' < 0
            then
              ( 0 - value' )
            else
              value';

    add#: int | float -> int | float -> int | float
    =   intrinsics.add or (a: b: ( assertNumber a ) + ( assertNumber b ));

    and#: int -> int -> int
    =   intrinsics.bitAnd;

    ceil#: int | float -> int
    =   intrinsics.ceil;

    div#: int | float -> int | float -> int | float
    =   intrinsics.div or (a: b: ( assertNumber a ) / ( assertNumber b ));

    floor#: int | float -> int
    =   intrinsics.floor;

    isInstanceOf
    =   value:
          integer.isInstanceOf value
          || float.isInstanceOf value;

    lessThan#: int | float -> int | float -> int | float
    =   intrinsics.lessThan or (a: b: ( assertNumber a ) < ( assertNumber b ));

    moreThan#: int | float -> int | float -> int | float
    =   a: b: ( assertNumber a ) > ( assertNumber b );

    mul#: int | float -> int | float -> int | float
    =   intrinsics.mul or (a: b: ( assertNumber a ) * ( assertNumber b ));

    neg#: int | float -> int | float
    =   value: ( 0 - ( assertNumber value ) );

    or#: int -> int -> int
    =   intrinsics.bitOr;

    orNull
    =   value:
          isInstanceOf value || value == null;

    pow#: int -> int | float -> int | float
    =   let
          pow#: int -> int | float -> int | float
          =   base:
              exp:
                list.fold
                  (y: x: x*y)
                  1.0
                  (list.generate (x: base) exp);
        in
          base:
          exp:
            if exp < 0.0
            then
              pow ( 1.0 / base ) ( 0 - exp )
            else
              pow ( 1.0 * base ) exp;

    round#: int | float -> int
    =   value:
          #debug.info "round" { data = { inherit value; value' = value + 0.5; round = floor ( value + 0.5 ); }; }
          matchNumber value
          {
            int                         =   value;
            float                       =   round' value;
          };

    round'#: float -> int
    =   value: floor ( value + 0.5 );

    sub#: int | float -> int | float -> int | float
    =   intrinsics.sub or (a: b: (assertNumber a) - (assertNumber b));

    sum#: [ int | float ] -> int | float
    =   list.fold (result: value: result + value) 0;

    toFloat#: int | float | string -> float
    =   value:
          type.matchPrimitiveOrDefault value
          {
            int                         =   1.0 * value;
            float                       =   value;
            /*string
            =   let
                  parts                   =   string.match "([0-9]*)[.](0*)([0-9]*)" value;
                  len                     =   string.length parts.dec;
                in
                  ( ( 1.0 * ( toInteger parts.dec ) ) / ( pow 10 len ) )
                  + ( toInteger parts.int );*/
          }
          ( debug.panic "toFloat" "Cannot convert ${type.getPrimitive value} to float." );

    toInteger#: int | float | string -> int
    =   value:
          type.matchPrimitiveOrDefault value
          {
            float                       =   round' value;
            int                         =   value;
            string
            =   let
                  result                =   toInteger' value;
                in
                  if result != null
                  then
                    result
                  else
                    debug.panic "toInteger" "Could not convert string ${value} to int!";
          }
          ( debug.panic "toInteger" "Could not convert type ${type value} to int!" );

    toInteger'#: string -> int | null
    =   value:
          let
            value'                      =   string.match "([+-])?0*([0-9.][0-9]+)" value;
            result                      =   expression.fromJSON ( list.get value' 1);
            sign                        =   list.head value';
          in
            if value' != null
            && integer.isInstanceOf result
            then
              if sign == "-"
              then
                ( - result )
              else
                result
            else
              null;

    toStringWithMaximumPrecision#: int | float -> int -> string
    =   value:
        precision:
          if precision > 0
          then
            list.foldReversed
              (
                state:
                character:
                  if state != null          then  "${character}${state}"
                  else if character == "."  then  ""
                  else if character != "0"  then  character
                  else                            null
              )
              null
              ( string.toCharacters ( toStringWithPrecision value precision ) )
          else
            toStringWithPrecision value precision;

    splitFloat
    =   value:
        precision:
          let
            factor                      =   pow 10 precision;
            value'                      =   string ( round ( value * factor ) );
            length                      =   string.length value';
            padding                     =   string.concat (list.generate (_: "0") (precision - length));
          in
            debug.debug "splitFloat"
            {
              text                      =   "called with/calculated:";
              data
              =   {
                    inherit value precision factor value' length;
                  };
            }
            (
              if length > precision
              then
                let
                  mid                   =   length - precision;
                  integer               =   string.slice  0   mid       value';
                  decimal               =   string.slice  mid precision value';
                in
                  debug.debug "splitFloat"
                  {
                    text                =   "length > precision";
                    data                =   { inherit integer decimal mid; };
                  }
                  { inherit decimal integer; }
              else
                let
                  integer               =   "0";
                  decimal               =   "${padding}${value'}";
                in
                  debug.debug "splitFloat"
                  {
                    text                =   "length <= precision";
                    data                =   { inherit integer decimal padding; };
                  }
                  { inherit decimal integer; }
            );

    toStringWithPrecision#: int | float -> int -> string
    =   value:
        precision:
          let
            precision'
            =   type.matchPrimitiveOrDefault precision
                {
                  int                   =   precision;
                  null                  =   getPrecision value;
                }
                ( debug.panic "toStringWithPrecision" "Invalid Precision: Int or null expected!" );
            valuePos                    =   splitFloat (0.0 + value)  precision';
            valueNeg                    =   splitFloat (0.0 - value)  precision';
            valueWithPrecision
            =   debug.info "toStringWithPrecision" { text = "value"; data = value; }
                debug.info "toStringWithPrecision" { text = "precision"; data = [ precision precision' ]; }
                (
                  if precision' == 0
                  then
                    "${string (round value)}"
                  else if value >= 0
                  then
                    "${valuePos.integer}.${valuePos.decimal}"
                  else
                    "-${valueNeg.integer}.${valueNeg.decimal}"
                );
          in
            type.matchPrimitiveOrDefault value
            {
              float                     =   valueWithPrecision;
              int                       =   valueWithPrecision;
              list                      =   string.concatMappedWith (x: toStringWithPrecision x precision) ", " value;
              set
              =   (
                    { from, till }:
                    "${toStringWithPrecision from precision}–${toStringWithPrecision till precision}"
                  )
                  value;
            }
            ( debug.panic "toStringWithPrecision" "Value must be a numeric value like int or float!" );

    toSignificantString#: int | float -> string
    =   value:
          toStringWithPrecision value null;

    getPrecision
    =   value:
          debug.info "getPrecision" { text = "value"; data = value; }
          (
            if  value == 0.0
            ||  value >= 2.0
            ||  value <= -2.0
            then
              0
            else
              (getPrecision (value * 10)) + 1
          );

    xor#: int -> int -> int
    =   intrinsics.bitXor;
  in
    type "number"
    {
      inherit isInstanceOf orNull;
      inherit abs add and ceil div floor lessThan moreThan mul neg or pow round round' sub sum xor;
      inherit toFloat toInteger toInteger' toSignificantString toStringWithMaximumPrecision toStringWithPrecision;
    }
