PreviousUpNext

15.4.1226  src/lib/std/src/two-word-int.pkg

## two-word-int.pkg
#
# 64-bit integers on 32-bit architectures, 128-bit integers on 64-bit architectures.

# Compiled by:
#     src/lib/std/src/standard-core.sublib



###               "The difference between the right word
###                and the almost right word is the difference
###                between lightning and a lightning bug."
###
###                                    -- Mark Twain



stipulate
    package lms =  list_mergesort;                                                                      # list_mergesort        is from   src/lib/src/list-mergesort.pkg
    package pb  =  proto_basis;                                                                         # proto_basis           is from   src/lib/std/src/proto-basis.pkg
    package rt  =  runtime;                                                                             # runtime               is from   src/lib/core/init/runtime.pkg
    package ti  =  inline_t::ti;                                                                        # "ti" == "tagged_int".
herein

    package two_word_int: (weak)  Int {                                                                 # Int                   is from   src/lib/std/src/int.api
        #                                                                                               # inline_t              is from   src/lib/core/init/built-in.pkg
        Int = two_word_int::Int;        
                                                                                                        # "i2" == "two-word int": 64-bits on 32-bit architectures, 128-bits on 64-bit architectures. 
        extern = inline_t::i2::extern;
        intern = inline_t::i2::intern;

        precision = THE 64;                                                                             # 64-bit issue -- this will be 128 on 64-bit architectures.

        my min_int_val:  Int = -0x8000000000000000;                                                     # 64-bit issue.

        my min_int:  Null_Or( Int ) = THE min_int_val;
        my max_int:  Null_Or( Int ) = THE 0x7fffffffffffffff;                                           # 64-bit issue.

        to_multiword_int   =  core_multiword_int::extend_inf64 o core_two_word_int::extern;
        from_multiword_int =  core_two_word_int::intern        o core_multiword_int::test_inf64;

        fun negbit hi = one_word_unt_guts::bitwise_and (hi, 0ux80000000);                               # 64-bit issue.
        fun isneg  hi = negbit hi != 0u0;

        fun to_int i
            =
            {   mask = 0uxc0000000;                                                                     # 64-bit issue.
                #
                case (extern i)
                    #
                    (0u0, lo)
                        =>
                        if (one_word_unt_guts::bitwise_and (lo, mask) == 0u0)
                            #
                            one_word_unt_guts::to_int lo;
                        else
                            raise exception rt::OVERFLOW;
                        fi;
                    #
                    (0uxffffffff, lo)                                                           # 64-bit issue.
                        =>
                        if (one_word_unt_guts::bitwise_and (lo, mask) == mask)
                            #
                            one_word_unt_guts::to_int_x lo;
                        else
                            raise exception rt::OVERFLOW;
                        fi;

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

        fun from_int tagged_int
            =
            {   i32 = one_word_int_guts::from_int tagged_int;
                #
                hi =    if (i32 < 0)    0uxffffffff;                                            # 64-bit issue.
                        else        0u0;
                        fi;
                #
                intern (hi, inline_t::u1::copyf_int1 i32);
            };

        fun quot (x, y)
            =
            from_multiword_int (multiword_int_guts::quot (to_multiword_int x, to_multiword_int y));


        fun rem (x, y)
            =
            x - quot (x, y) * y;


        fun sign 0 => 0;
            sign i => if (isneg (#1 (extern i)))   -1;
                      else                          1;
                      fi;
        end;

        fun same_sign (x, y)
            =
            sign x == sign y;

        fun min (x: Int, y) = if (x < y) x; else y; fi;
        fun max (x: Int, y) = if (x > y) x; else y; fi;

        fun compare (x, y)
            =
            {   my (hi1, lo1) = extern x;
                my (hi2, lo2) = extern y;

                fun normal ()   #  same-sign case 
                    =
                    if   (hi1 < hi2) LESS;
                    elif (hi1 > hi2) GREATER;
                    elif (lo1 < lo2) LESS;
                    elif (lo1 > lo2) GREATER;
                    else             EQUAL;
                    fi;

               if (isneg hi1)
                   if (isneg hi2 ) normal ();
                   else LESS;
                   fi;
               elif (isneg hi2 ) GREATER;
               else              normal ();
               fi;
            };


        fun format  rdx  i
            =
            multiword_int_guts::format  rdx  (to_multiword_int i);


        to_string =  format  number_string::DECIMAL;


        fun scan rdx rdr s
            =
            case (multiword_int_guts::scan  rdx  rdr  s)
                #          
                THE (i, s')
                    =>
                    if (i < -0x80000000 or i > 0x7fffffff)                                              # 64-bit issue.
                        #                            
                        raise exception rt::OVERFLOW;
                    else
                        THE (intern (core_multiword_int::trunc_inf64 i), s');
                    fi;

                NULL => NULL;
            esac;

        from_string
            =
            pb::scan_string (scan number_string::HEX);

        my (-_)   : Int -> Int        = (-_);
        my neg    : Int -> Int        = (-_);

        my (+)    : (Int, Int) -> Int  = (+);
        my (-)    : (Int, Int) -> Int  = (-);
        my (*)    : (Int, Int) -> Int  = (*);
        my (/)    : (Int, Int) -> Int  = (/);
        my (%)    : (Int, Int) -> Int  = (%);

        my abs    : Int -> Int         = abs;

        my (<)    : (Int, Int) -> Bool = (<);
        my (<=)   : (Int, Int) -> Bool = (<=);
        my (>)    : (Int, Int) -> Bool = (>);
        my (>=)   : (Int, Int) -> Bool = (>=);

        fun 0! =>  1;
            n! =>  n * (n - 1)! ;
        end;

        fun is_prime p                  # A very simple and naive primality tester.  2009-09-02 CrT.
            =
            {   p = abs(p);                     # Try to do something reasonable with negative numbers.

                if   (p < 4)       TRUE;        # Call zero prime.
                elif (p % 2 == 0)  FALSE;       # Special-case even numbers to halve our loop time.
                else
                    # Test all odd numbers less than sqrt(p):

                    loop 3
                    where
                        fun loop i
                            =
                            if   (p % i == 0)   FALSE;
                            elif (i*i >= p)     TRUE;
                            else                loop (i + 2);
                            fi;
                    end;
                fi;
            };

        fun factors n
            =
            factors' (n, 2, [])
            where
                fun factors' (n, p, results)
                    =
                    if (p*p > n)
                        #
                        reverse (n ! results);

                    elif (n % p == 0)

                       factors' (n/p, p,   p ! results);

                    else

                       factors' (n,   p+1,     results);
                    fi;
            end;

        fun sum ints
            =
            sum' (ints, 0)
            where
                fun sum' (      [], result) =>  result;
                    sum' (i ! rest, result) =>  sum' (rest, i + result);
                end;
            end;

        fun product ints
            =
            product' (ints, 1)
            where
                fun product' (      [], result) =>  result;
                    product' (i ! rest, result) =>  product' (rest, i * result);
                end;
            end;

        fun list_min [] =>   raise exception DIE "Cannot do list_min on empty list";
            #
            list_min (i ! ints)
                =>
                min' (ints, i: Int)
                where
                    fun min' (      [], result) =>  result;
                        min' (i ! rest, result) =>  min'  (rest,  i < result ?? i :: result);
                    end;
                end;
        end;

        fun list_max [] =>   raise exception DIE "Cannot do list_max on empty list";
            #
            list_max (i ! ints)
                =>
                min' (ints, i: Int)
                where
                    fun min' (      [], result) =>  result;
                        min' (i ! rest, result) =>  min'  (rest,  i > result ?? i :: result);
                    end;
                end;
        end;

        fun sort ints
            =
            lms::sort_list (>) ints;

        fun sort_and_drop_duplicates ints
            =
            lms::sort_list_and_drop_duplicates  compare  ints;

        fun mean []     =>      0;                                                      # Would throwing an exception be better?  In graphics, at least, often it is better to just gloss over the occasional special case...
            mean ints   =>      sum ints   /   from_int (length ints);
        end;


        fun median []
                =>
                0;                                                                      # As above, arbitrary, possibly should throw exception.

            median ints
                =>
                {   len  = length ints;
                    ints = lms::sort_list (>) ints;
                    #
                    i1 = ti::div (len, 2);
                    i2 = ti::(-) (i1,  1);

                    if (is_odd(len))
                        #       
                        # Return middle element:
                        #       
                        list::nth (ints, i1);
                    else
                        # Return average of the two middle elements:
                        #
                        n1 = list::nth (ints, i1); 
                        n2 = list::nth (ints, i2); 

                        (n1 + n2) / 2;
                    fi;
                }
                where
                    fun is_odd(i) =  (ti::bitwise_and(i, 1) == 1);
                end;
        end;
    };
end;



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext