PreviousUpNext

15.4.713  src/lib/core/init/core-multiword-int.pkg

## core-multiword-int.pkg
#
# Basic integer functionality for translating certain primops and
# multiword_int::Int literals within the compiler.  This gets expanded
# into a full-functionality indefinite-precision integer arithmetic
# package in
#
#     src/lib/std/src/multiword-int-guts.pkg

# Compiled by:
#     src/lib/core/init/init.cmi






package core_multiword_int: api {

                                    # We use a 30-bit representation, stored in 31-bit words for digits.
                                    # This way we avoid the extra boxing that would come with 32-bit values
                                    # and also have the benefit of an extra bit that can be used to store
                                    # carries.

                                    Rep = BI  { negative:       Bool,
                                                digits:         List( Unt )
                                              };

                                    # This is the abstract built-in type "integer".  It comes with no
                                    # operations of its own.  We use casts to go back and forth between
                                    # the representation type "rep" and the abstract type "integer".

                                    Multiword_Int =   base_types::Multiword_Int;

                                    #  Here are the "cast" operations: 
                                    abstract:  Rep    -> Multiword_Int;
                                    concrete:  Multiword_Int -> Rep;

                                    base_bits:  Unt;                                    # The number of bits in one "big digit" 

                                    base:  Unt;                                         # The actual base. 

                                    max_digit:  Unt;                                    # Maximum value of a "big digit". 

                                    # The following three functions are copied into package _Core
                                    # from where the compiler's "translate" phase will pick them to
                                    # implement precision-extending and precision-shrinking conversions
                                    # that involve Multiword_Int.  We only provide operations between
                                    # Int1 and Multiword_Int.  For precisions less than 32 bits the compiler
                                    # must insert an additional conversion:

                                    test_inf:    Multiword_Int -> Int1;                 # fit value (2's complement) in Int1, raise OVERFLOW if too large 
                                    trunc_inf:   Multiword_Int -> Int1;                 #  truncate value (2's complement repr) to fit in Int1: 
                                    copy_inf:    Int1 -> Multiword_Int;                 #  Copy bits from Int1 into (non-negative) Multiword_Int: 

                                    extend_inf:  Int1 -> Multiword_Int;                 #  sign-extend Int1: 
                                    fin_to_inf:   (Int1, Bool) -> Multiword_Int;        #  Combined (sign-extension takes place when second argument is TRUE): 
                                    test_inf64:    Multiword_Int -> (Unt1, Unt1);       #  fit value (2's complement) in "two_word_int", raise OVERFLOW if too large 

                                    trunc_inf64:   Multiword_Int -> (Unt1, Unt1);       #  truncate value (2's complement repr) to fit in "two_word_int": 
                                    copy_inf64:    (Unt1, Unt1) -> Multiword_Int;       #  Copy bits from "two_word_int" into (non-negative) Multiword_Int: 
                                    extend_inf64:  (Unt1, Unt1) -> Multiword_Int;       #  sign-extend "two_word_int": 

                                    fin_to_inf64:   (Unt1, Unt1, Bool) -> Multiword_Int;        #  Combined (sign-extension takes place when second argument is TRUE): 

                                    # These two directly take the list of digits
                                    # to be used by the internal representation:
                                    #
                                    make_neg_inf:  List( Unt ) -> Multiword_Int;
                                    make_pos_inf:  List( Unt ) -> Multiword_Int;

                                    # In the common case where only one big digit is involved,
                                    # use a shortcut without list allocation:
                                    #
                                    make_small_neg_inf:  Unt -> Multiword_Int;
                                    make_small_pos_inf:  Unt -> Multiword_Int; 

                                    # For -base < i < base we have low_value i = i.
                                    # For other i we have low_value i = -base (= neg_base_as_int).
                                    # This can be used to implement faster pattern-match code for
                                    # the common case that the pattern consists of small values.
                                    #
                                    low_value:  Multiword_Int -> Int;
                                    neg_base_as_int:  Int;

                                    # Various primitive operations for use by the pervasive dictionary,
                                    # plus stuff that we have to implement here anyway, so the
                                    # real package integer can pick them up:
                                    #
                                    (-_) : Multiword_Int -> Multiword_Int;
                                    neg  : Multiword_Int -> Multiword_Int;
                                    + : (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    - : (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    * : (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    div:  (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    mod:  (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    quot:  (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    rem:  (Multiword_Int, Multiword_Int) -> Multiword_Int;
                                    < : (Multiword_Int, Multiword_Int) -> Bool;
                                    <= : (Multiword_Int, Multiword_Int) -> Bool;
                                    > : (Multiword_Int, Multiword_Int) -> Bool;
                                    >= : (Multiword_Int, Multiword_Int) -> Bool;
                                    compare:  (Multiword_Int, Multiword_Int) -> order::Order;
                                    abs:  Multiword_Int -> Multiword_Int;
                                    pow:  (Multiword_Int, Int) -> Multiword_Int;

                                    div_mod:   (Multiword_Int, Multiword_Int) -> (Multiword_Int, Multiword_Int);
                                    quot_rem:  (Multiword_Int, Multiword_Int) -> (Multiword_Int, Multiword_Int);

                                    # Support for scanning and formatting:
                                    #
                                    natdivmodd:  (List( Unt ), Unt) -> (List( Unt ), Unt);
                                    natmadd:  (Unt, List( Unt ), Unt) -> List( Unt );

                                }
{
    infixr my 60 ! ;

    not = inline::not_macro;

    w31to_i32  = inline::copy_31_32_i;
    w32to_i32  = inline::copy_32_32_ui;
    w31to_w32  = inline::copy_31_32_u;
    i32to_w31  = inline::trunc_32_31_i;
    i32to_w32  = inline::copy_32_32_iu;
    w32to_w31  = inline::trunc_32_31_u;

    my (-_) = inline::i1_negate;
    my neg  = inline::i1_negate;

    infix my | & >> ^ <<;

    (|)  = inline::u1_bitwise_or;
    (^)  = inline::u1_bitwise_xor;
    (&)  = inline::u1_bitwise_and;
    (>>) = inline::u1_rshiftl;
    (<<) = inline::u1_lshift;

    #  ******************* 
    Multiword_Int =   base_types::Multiword_Int;

    # assuming 30-bit digits (must match actual implementation),
    # least significant digit first,
    # normalized (last digit != 0)

    Rep = BI  { negative:  Bool, digits:  List( Unt ) };

    fun abstract (r:  Rep    ) : Multiword_Int = inline::cast r;
    fun concrete (i:  Multiword_Int) : Rep     = inline::cast i;

    my h_base_bits:  Unt = 0u15;
    my base_bits:  Unt = inline::tu1_lshift (h_base_bits, 0u1);
    my max_digit:  Unt = 0ux3fffffff;
    max_digit32 = w31to_w32 max_digit;
    my max_digit_l:  Unt = 0ux7fff;     #  lower half of maxDigit 
    max_digit_l32 = w31to_w32 max_digit_l;
    my base:  Unt = 0ux40000000;
    base32 = w31to_w32 base;
    my neg_base_as_int:  Int = -0x40000000;

    gap = inline::tu1_subtract (0u32, base_bits); #  32 - base_bits 
    slc = inline::tu1_subtract (base_bits, gap);  #  BaseBits - gap 

    fun neg64 (hi, 0u0) =>  (inline::u1_negate hi, 0u0);
        neg64 (hi,  lo) =>  (inline::u1_bitwise_not hi, inline::u1_negate lo);
    end;
              
    fun test_inf i
        =
        {   (concrete i) ->   BI { negative, digits };
            #
            fun negif i32
                =
                if negative  -i32;
                else          i32;
                fi;

            case digits
                #              
                []         =>   0;
                [d]        =>   negif (w31to_i32 d);
                [d0, 0u1]  =>   negif (w32to_i32 (w31to_w32 d0 | base32));

                [0u0, 0u2] =>   if negative  -0x80000000;
                                else         raise exception runtime::OVERFLOW;
                                fi;

                _ => raise exception runtime::OVERFLOW;
            esac;
        };

    fun test_inf64 i
        =
        {   (concrete i) ->   BI { negative, digits };
            #
            fun negif hilo
                =
                if negative   neg64 hilo;
                else                hilo;
                fi;

            case digits
                #              
                []  =>  (0u0, 0u0);
                [d] =>  negif (0u0, w31to_w32 d);

                [d0, d1]
                    =>
                    {   my (w0, w1) = (w31to_w32 d0, w31to_w32 d1);
                        #
                        negif (w1 >> gap, w0 | (w1 << base_bits));
                    };

                [0u0, 0u0, 0u8]
                    =>
                    if negative  (0ux80000000, 0u0);
                    else         raise exception runtime::OVERFLOW;
                    fi;

                [d0, d1, d2]
                    =>
                    if (inline::tu1_ge (d2, 0u8))
                        #
                        raise exception runtime::OVERFLOW;
                    else
                        w0 =  w31to_w32  d0;
                        w1 =  w31to_w32  d1;
                        w2 =  w31to_w32  d2;

                        negif ((w1 >> gap) | (w2 << slc),
                              w0 | (w1 << base_bits));
                    fi;

                _   => raise exception runtime::OVERFLOW;
            esac;
        };

    fun trunc_inf i
        =
        {   (concrete i) ->   BI { negative, digits };
            #
            b = case digits
                    #                  
                    []          =>   0u0;
                    [d]         =>   w31to_w32 d;
                    d0 ! d1 ! _ =>   w31to_w32 d0 | (w31to_w32 d1 << base_bits);
                esac;

            w32to_i32   if negative  inline::u1_negate b;
                        else                           b;
                        fi;
        };

    fun trunc_inf64 i
        =
        {   (concrete i) ->    BI { negative, digits };
            #
            hilo =  case digits
                        #                  
                        []   =>  (0u0, 0u0);
                        [d0] =>  (0u0, w31to_w32 d0);

                        [d0, d1]
                            =>
                            {   my (w0, w1) = (w31to_w32 d0, w31to_w32 d1);
                                #
                                (w1 >> gap, w0 | (w1 << base_bits));
                            };

                       d0 ! d1 ! d2 ! _
                            =>
                            {   my (w0, w1, w2)
                                   =
                                   (w31to_w32 d0, w31to_w32 d1, w31to_w32 d2);

                                ((w1 >> gap) | (w2 << slc), w0 | (w1 << base_bits));
                            };
                    esac;

            if negative    neg64 hilo;
            else                 hilo;
            fi;
        };

    fun extend_inf i32
        =
        abstract  if   (inline::i1_eq (i32, -0x80000000) )  BI { negative => TRUE, digits => [0u0, 0u2 ] };
                  elif (inline::i1_lt (i32, 0) )            e (TRUE, i32to_w31 (-i32));
                  else                                      e (FALSE, i32to_w31 i32);
                  fi
        where
            fun e (_, 0u0)
                    =>
                    BI { negative => FALSE, digits => [] };

                e (negative, w31)
                    =>
                    BI { negative,
                         #
                         digits   => if (inline::tu1_ge (w31, base))    [inline::tu1_subtract (w31, base), 0u1];
                                     else                              [w31];
                                     fi
                       };
            end;
        end;


    fun copy_inf64' (_, (0u0, 0u0))
            =>
            abstract (BI { negative => FALSE,
                                                 digits => [] } );
        copy_inf64' (negative, (hi, lo))
            => 
            {   infix my <>;
                #
                (<>) =  inline::tu1_ne;

                d0 = w32to_w31 (lo & 0ux3fffffff);
                d1 = w32to_w31 (((hi & 0uxfffffff) << 0u2) | (lo >> 0u30));
                d2 = w32to_w31 (hi >> 0u28);

                abstract (BI { negative,
                               digits => if   (d2 <> 0u0 ) [d0, d1, d2];
                                         elif (d1 <> 0u0 ) [d0, d1];
                                         elif (d0 <> 0u0 ) [d0];
                                         else [];
                                         fi
                              }
                          );
            };
    end;

    fun copy_inf64 hilo
        =
        copy_inf64' (FALSE, hilo);

    fun extend_inf64 (hi, lo)
        =
        if (inline::u1_ne (hi & 0ux80000000, 0u0))
             #            
             copy_inf64' (TRUE, neg64 (hi, lo));
        else copy_inf64' (FALSE,      (hi, lo));
        fi;

    fun copy_inf i32
        =
        {   w32 = i32to_w32 i32;
            #
            digits =    if   (inline::u1_eq (w32, 0u0) ) [];
                        elif (inline::u1_ge (w32, base32) )
                            [w32to_w31 (w32 & max_digit32), w32to_w31 (w32 >> base_bits)];
                        else [w32to_w31 w32];
                        fi;

            abstract (BI { negative => FALSE, digits } );
        };

    fun fin_to_inf (i32, extend)
        =
        if extend  extend_inf i32;
        else         copy_inf i32;
        fi;

    fun fin_to_inf64 (hi, lo, extend)
        =
        if extend  extend_inf64 (hi, lo);
        else         copy_inf64 (hi, lo);
        fi;

    fun make_inf negative digits
        =
        abstract (BI { negative, digits } );

    make_neg_inf = make_inf TRUE;
    make_pos_inf = make_inf FALSE;

    fun make_small_inf _ 0u0
            =>
            abstract (BI { negative => FALSE, digits => [] } );

       make_small_inf negative digit
           =>
           abstract (BI { negative, digits => [digit] } );
    end;

    make_small_neg_inf =  make_small_inf TRUE;
    make_small_pos_inf =  make_small_inf FALSE;

    fun low_value i
        =
        case (concrete i)
            #          
            BI { digits => [], ... }                =>   0;
            BI { digits => [d], negative => FALSE } =>   inline::copy_31_31_ui d;
            BI { digits => [d], negative => TRUE  } =>   inline::ti1_negate (inline::copy_31_31_ui d);
            _                                       =>   neg_base_as_int;
       esac;

    # Concrete->abstract wrappers for unary and binary functions 
    #
    fun fabs1  f  x     =  abstract (f (concrete x));
    fun fabs2  f (x, y) =  abstract (f (concrete x, concrete y));
    fun fabs2c f (x, y) =            f (concrete x, concrete y);

    fun fabs22 f (x, y)
        =
        {   my (a, b) =  f (concrete x, concrete y);
            #
            (abstract a,  abstract b);
        };

    # Like BI, but make sure that digits = [] implies not negative 
    #
    fun bi { digits => [], ... } => BI { digits => [], negative => FALSE };
        bi arg => BI arg;
    end;

    fun abs' (BI { negative, digits } )
        =
        BI { negative => FALSE, digits };

    fun neg (BI { digits, negative } )
        =
        bi { digits, negative => not negative };

    include package   order;
             
    fun dcmp (x, y)
        =
        if   (inline::tu1_lt (x, y))  LESS;
        elif (inline::tu1_gt (x, y))  GREATER;
        else                         EQUAL;
        fi;

    fun natcmp ([], []) =>  EQUAL;
        natcmp ([], _)  =>  LESS;
        natcmp (_, [])  =>  GREATER;

        natcmp (x ! xs, y ! ys)
            =>
            case (natcmp (xs, ys))
              
                EQUAL   =>  dcmp (x, y);
                unequal =>  unequal;
            esac;
    end;

    fun lt (BI { negative => FALSE, ... },
            BI { negative => TRUE, ... } )
            =>
            FALSE;

        lt (BI { negative => TRUE, ... },
            BI { negative => FALSE, ... } )
            =>
            TRUE;

        lt (BI { negative, digits => d1 },
            BI { digits => d2, ... } )
            =>
            (inline::(==)) (natcmp (d1, d2), if negative  GREATER; else LESS;fi);
    end;

    fun gt (x, y) =  lt (y, x);
    fun ge (x, y) =  not (lt (x, y));
    fun le (x, y) =  not (gt (x, y));

    fun adddig (d1, d2)
        =
        {   sum = inline::tu1_add (d1, d2);

            { carry  =>  inline::tu1_ge (sum, base),
              result =>  inline::tu1_bitwise_and (sum, max_digit)
            };
        };

    # Add one to nat:
    # 
    fun natinc [] => [0u1];
        #
        natinc (x ! xs)
            =>
            if (inline::tu1_eq (x, max_digit))     0u0 ! natinc xs;
            else                                  inline::tu1_add (x, 0u1) ! xs;
            fi;
    end;

    fun natdec (0u0 ! xs) =>  max_digit ! natdec xs;
        natdec [0u1]      =>  [];
        natdec (x ! xs)   =>  inline::tu1_subtract (x, 0u1) ! xs;
        natdec []         =>  raise exception runtime::OVERFLOW;                        # Should never happen! 
    end;


    # Add two nats plus 1 (carry):
    #
    fun natadd1 (x, []) =>  natinc x;
        natadd1 ([], y) =>  natinc y;
        #
        natadd1 (x ! xs, y ! ys)
            =>
            {   (adddig (x, y)) ->   { carry, result };

                my (carry, result)
                    =
                    if (inline::tu1_eq (result, max_digit))     (TRUE, 0u0);
                    else                                       (carry, inline::tu1_add (result, 0u1));
                    fi;

                result ! natadd01 (carry, xs, ys);
            };
    end 

    # Add two nats:
    #
    also
    fun natadd0 (x, []) =>  x;
        natadd0 ([], y) =>  y;
        #
        natadd0 (x ! xs, y ! ys)
            =>
            {   (adddig (x, y)) ->   { carry, result };
                #
                result  !  natadd01 (carry, xs, ys);
            };
    end 

    # Add two nats with optional carry:
    #
    also
    fun natadd01 (carry, xs, ys)
         =
         if carry   natadd1 (xs, ys);
         else       natadd0 (xs, ys);
         fi;

    exception NEGATIVE;

    # natsub hopes that xs >= ys + carry, raises NEGATIVE otherwise 
    #
    fun natsub (xs, [], FALSE) => xs;
        natsub (xs, [], TRUE) => natsub (xs, [0u0], TRUE);
        natsub ([], _, _) => raise exception NEGATIVE;

        natsub (x ! xs, y ! ys, c)
            =>
            {   y' =  if c    inline::tu1_add (y, 0u1);
                      else    y;
                      fi;

                my (result, carry)
                    =
                    if (inline::tu1_lt (x, y'))    (inline::tu1_subtract (inline::tu1_add (x, base), y'), TRUE);
                    else                          (inline::tu1_subtract (x, y'), FALSE);
                    fi;

                case (natsub (xs, ys, carry))
                    #                  
                    []   =>  if (inline::tu1_eq (result, 0u0))    [];
                             else                                [result];
                             fi;

                    more =>  result ! more;
                esac;
            };
    end;



    fun natsub0 (xs, ys)
        =
        natsub (xs, ys, FALSE);



    fun sub0 (xs, ys)
        =
        BI { negative => FALSE,
             digits   => natsub0 (xs, ys)
           }
        except
            NEGATIVE =  bi { negative => TRUE, digits => natsub0 (ys, xs) };



    fun add0 (FALSE, xs1, FALSE, xs2)
            =>
            BI { negative => FALSE, digits => natadd0 (xs1, xs2) };

       add0 (TRUE, xs1, TRUE, xs2)
           =>
           BI { negative => TRUE, digits => natadd0 (xs1, xs2) };

       add0 (FALSE, xs1, TRUE, xs2)
           =>
           sub0 (xs1, xs2);

       add0 (TRUE, xs1, FALSE, xs2)
           =>
           sub0 (xs2, xs1);
    end;



    fun add (BI b1, BI b2)
        =
        add0 (b1.negative, b1.digits, b2.negative, b2.digits);



    fun sub (BI b1, BI b2)
        =
        add0 (b1.negative, b1.digits, not b2.negative, b2.digits);



    fun compare' (BI { negative => TRUE, ... }, BI { negative => FALSE, ... } )
            =>
            LESS;

        compare' (BI { negative => FALSE, ... }, BI { negative => TRUE, ... } )
            =>
            GREATER;

        compare' (BI { negative, digits => d1 }, BI { digits => d2, ... } )
            =>
            if negative  natcmp (d2, d1);
            else         natcmp (d1, d2);
            fi;
    end;

    fun ddmul (x, y)
        =
        {   fun high w32 =  w32 >> h_base_bits;
            fun low  w32 =  w32 & max_digit_l32;
            fun hl   w32 = (high w32, low w32);

            (hl (w31to_w32 x)) ->   (xh, xl);
            (hl (w31to_w32 y)) ->   (yh, yl);

            a = inline::u1_mul (xh, yh);
            c = inline::u1_mul (xl, yl);

            #  B = b' - a - c = xh * yl + xl * yh 
            #
            b' = inline::u1_mul (inline::u1_add (xh, xl), inline::u1_add (yh, yl));
            b  = inline::u1_subtract (b', inline::u1_add (a, c));

            (hl b) ->   (bh, bl);

            l0 = inline::u1_add (c, inline::u1_lshift (bl, h_base_bits));
            l32 = l0 & max_digit32;
            lc = l0 >> base_bits;
            h32 = inline::u1_add (inline::u1_add (a, bh), lc);

            (w32to_w31 h32, w32to_w31 l32);
        };

    fun natmadd (0u0, _,  0u0) =>  [];
        natmadd (0u0, _,    c) =>  [c];
        natmadd (_, [],   0u0) =>  [];
        natmadd (_, [],     c) =>  [c];
        natmadd (0u1, xs, 0u0) =>  xs;
        natmadd (0u1, xs,   c) =>  natadd0 (xs, [c]);
        #
        natmadd (w, x ! xs, c)
            =>
            {   (ddmul (w, x))  ->   (h, l);
                (adddig (l, c)) ->   { carry,  result => l' };
                #
                h' =    if carry  inline::tu1_add (h, 0u1);
                        else      h;
                        fi;

                l' ! natmadd (w, xs, h');
            };
    end;

    fun natmul ([], _) =>  [];
        natmul (_, []) =>  [];
        #
        natmul (xs, [0u1]) =>  xs;
        natmul ([0u1], ys) =>  ys;
        #
        natmul (x ! xs, ys)
            =>
            natadd0 (natmadd (x, ys, 0u0), 0u0 ! natmul (xs, ys));
    end;

    fun mul (BI x, BI y)
        =
        bi { negative => (inline::(!=)) (x.negative, y.negative),
             digits => natmul (x.digits, y.digits)
           };

    one  = BI { negative => FALSE, digits => [0u1] };
    zero = BI { negative => FALSE, digits => []    };

    fun posi digits =  BI { digits, negative => FALSE };
    fun negi digits =  BI { digits, negative => TRUE };
    fun zneg digits =  bi { digits, negative => TRUE };

    fun consd (0u0, []) => [];
        consd (x, xs) => x ! xs;
    end;

    fun scale w
        =
        inline::tu1_div (base, inline::tu1_add (w, 0u1));

    # Return length-1 and last element:
    #
    fun length'n'last []  =>  (0, 0u0);         # Should not happen. 
        length'n'last [x] =>  (0, x);

        length'n'last (_ ! l)
            =>
            {   (length'n'last l) ->   (len, last);
                #
                (inline::ti1_add (len, 1), last);
            };
    end;

    fun nth (_, []) => 0u0;
        nth (0, x ! _ ) =>  x;
        nth (n, _ ! xs) =>  nth (inline::ti1_subtract (n, 1), xs);
    end;

    # Divide DP number by digit; assumes u < i, i >= base/2:
    #
    fun natdivmod2 ((u, v), i)
        =
        {   fun low  w =  inline::tu1_bitwise_and    (w, max_digit_l);
            fun high w =  inline::tu1_rshiftl (w, h_base_bits);

            my (vh, vl) = (high v, low v);
            my (ih, il) = (high i, low i);

            fun adj (q, r, vx)
                =
                loop (q, x)
                where
                    x = inline::tu1_add (inline::tu1_lshift (r, h_base_bits), vx);
                    y = inline::tu1_mul (q, il);

                    fun loop (q, x)
                        =
                        if (inline::tu1_ge (x, y))    (q, inline::tu1_subtract (x, y));
                        else                       loop (inline::tu1_subtract (q, 0u1), inline::tu1_add (x, i));
                        fi;
                end;

            q1 = inline::tu1_div (u, ih);
            r1 = inline::tu1_mod (u, ih);

            (adj (q1, r1, vh)) ->   (q1, r1);

            q0 = inline::tu1_div (r1, ih);
            r0 = inline::tu1_mod (r1, ih);

            (adj (q0, r0, vl)) ->   (q0, r0);

            (inline::tu1_add (inline::tu1_lshift (q1, h_base_bits), q0), r0);
        };

    # Divide bignat by digit > 0:
    #
    fun natdivmodd (m, 0u1) => (m, 0u0);                #  speedup 
        #
        natdivmodd (m, i)
            =>
            { 
                scale = scale i;
                i' = inline::tu1_mul (i, scale);
                m' = natmadd (scale, m, 0u0);

                fun dmi [] => ([], 0u0);
                    #
                    dmi (d ! r)
                        =>
                        {   my (qt, rm) = dmi r;
                            my (q1, r1) = natdivmod2 ((rm, d), i');

                            (consd (q1, qt), r1);
                        };
                end;

                (dmi m') ->   (q, r);

                (q, inline::tu1_div (r, scale));
            };
    end;

    # From Knuth Vol II, 4.3.1, but without opt. in step D3:
    #
    fun natdivmod (m, []) => raise exception runtime::DIVIDE_BY_ZERO;

        natdivmod ([], n) => ([], []); #  speedup 

        natdivmod (d ! r, 0u0 ! s)
            =>
            { 
                my (qt, rm) = natdivmod (r, s);
                (qt, consd (d, rm));
            }; #  speedup 

        natdivmod (m, [d])
            =>
            { 
                my (qt, rm) = natdivmodd (m, d);
                (qt, if (inline::tu1_eq (rm, 0u0) ) []; else [rm];fi);
            };

        natdivmod (m, n)
            =>
            {   my (ln, last) = length'n'last n; #  ln >= 1 

                scale = scale last;

                m' = natmadd (scale, m, 0u0);
                n' = natmadd (scale, n, 0u0);

                n1 = nth (ln, n');      #  >= base/2 

                fun divl [] => ([], []);

                    divl (d ! r)
                        =>
                        {
                             my (qt, rm) = divl r;
                             m = consd (d, rm);

                             fun msds ([], _) => (0u0, 0u0);
                                 msds ([d], 0) => (0u0, d);
                                 msds ([d2, d1], 0) => (d1, d2);
                                 msds (d ! r, i) => msds (r, inline::ti1_subtract (i, 1));
                             end;

                             my (m1, m2) = msds (m, ln);

                             tq = if (inline::(==) (m1, n1))  max_digit;
                                  else                        #1 (natdivmod2 ((m1, m2), n1));
                                  fi;

                             fun try (q, qn')
                                 =
                                 (q, natsub0 (m, qn'))
                                 except
                                     NEGATIVE
                                         =
                                         try (inline::tu1_subtract (q, 0u1),
                                                         natsub0 (qn', n'));

                             my (q, rr) = try (tq, natmadd (tq, n', 0u0));

                             (consd (q, qt), rr);
                        };
                end;

                my (qt, rm') = divl m';
                my (rm, _/*0*/) = natdivmodd (rm', scale);

                (qt, rm);
            };
    end;

    fun quot_rem' (_, BI { digits => [], ... } )
            =>
            raise exception runtime::DIVIDE_BY_ZERO;

        quot_rem' (BI { digits => [], ... }, _)
            =>
            (zero, zero);

        quot_rem' (BI x, BI y)
            =>
            {
                my (q, r) = natdivmod (x.digits, y.digits);

                # Construct return tuple:
                #
                ( if (((inline::(!=)) (x.negative, y.negative)))  zneg q; else posi q;fi,

                  if x.negative  zneg r; else posi r;fi
                );
            };
    end;

    fun div_mod' (_, BI { digits => [], ... }   ) =>  raise exception runtime::DIVIDE_BY_ZERO;
        div_mod' (   BI { digits => [], ... }, _) =>  (zero, zero);

        div_mod' (BI x, BI y)
            =>
            if   (inline::(==) (x.negative, y.negative))
                
                 my (q, r) = natdivmod (x.digits, y.digits);

                 (posi q, if x.negative  zneg r; else posi r;fi);
                
            else
                 my (m, n) = (x.digits, y.digits);
                 my (q, r) = natdivmod (natdec m, n);
                 mdd = natsub (n, r, TRUE);

                 (negi (natinc q),
                 if x.negative  posi mdd; else zneg mdd;fi);
            fi;
    end;

    fun div'  arg =  #1 (div_mod' arg);
    fun quot' arg =  #1 (quot_rem' arg);

    # For div and mod we special-case a divisor of 2 (common even-odd test):
    #
    fun mod' (BI { digits => [], ... }, _) => zero;
        mod' (BI { digits => low ! _, ... },
               BI { digits => [0u2], negative } )
            =>
            if (inline::tu1_eq (inline::tu1_bitwise_and (low, 0u1), 0u0) ) zero;
            else BI { digits => [0u1], negative };
            fi;

        mod' arg => #2 (div_mod' arg);
    end;

    fun rem' (BI { digits => [], ... }, _) => zero;

        rem' (BI { digits => low ! _, negative },
               BI { digits => [0u2], ... } )
            =>
           if (inline::tu1_eq (inline::tu1_bitwise_and (low, 0u1), 0u0) )
                zero;
           else BI { digits => [0u1], negative };
           fi;

        rem' arg =>  #2 (quot_rem' arg);
    end;

    fun natpow (_,  0) =>   [ 0u1 ];
        #
        natpow ([], n) =>   if (inline::ti1_lt (n, 0))   raise exception runtime::DIVIDE_BY_ZERO;   else [];   fi;
        #
        natpow (x, n)
            =>
            if (inline::ti1_lt (n, 0))
                #                
                [];
            else
                exp (x, inline::copy_31_31_iu n)
                where
                    fun exp (m, 0u0) => [0u1];
                        exp (m, 0u1) => m;
                        exp (m, n) => {
                             x = exp (m, inline::tu1_rshiftl (n, 0u1));
                             y = natmul (x, x);

                             if (inline::tu1_eq (inline::tu1_bitwise_and (n, 0u1), 0u0))     y;
                             else                                                   natmul (y, m);
                             fi;
                         };
                    end;
                end;
            fi;
    end;

    fun pow (_, 0)
            =>
            abstract (BI { negative => FALSE, digits => [0u1] } );
        #
        pow (i, n)
            =>
            {   (concrete i) ->   BI { negative, digits };
                #
                abstract (bi { negative => negative and  inline::ti1_eq (inline::ti1_rem (n, 2), 1),
                               digits   => natpow (digits, n)
                             }
                         );
            };
    end;

    (-_) =  fabs1 neg;
    neg  =  fabs1 neg;
    (-)  =  fabs2 sub;
    (+)  =  fabs2 add;
    (*)  =  fabs2 mul;

    div  = fabs2 div';
    mod  = fabs2 mod';
    quot = fabs2 quot';
    rem  = fabs2 rem';

    div_mod  = fabs22 div_mod';
    quot_rem = fabs22 quot_rem';

    (<)  = fabs2c lt;
    (>)  = fabs2c gt;
    (<=) = fabs2c le;
    (>=) = fabs2c ge;

    abs = fabs1 abs';

    compare = fabs2c compare';
};


## (C) 2003 The SML/NJ Fellowship.
## Author: Matthias Blume (blume@tti-c::org)
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext