Version française
Home     About     Download     Resources     Contact us    
Browse thread
BigNums in Caml Light 0.5
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: HEDDEN@E...
Subject: BigNums in Caml Light 0.5
Dear Caml Light users,

I would like to see some form of "arbitrary precision" math or
"big nums" added to Caml Light.  Below is a very preliminary
attempt at such an implementation written in Caml Light.  At this
point, I am not so much concerned with speed as I am with
obtaining the capabilities. 

I am using vectors and a "base 10000" implementation to represent
floating point numbers.  So far I have written routines for +, -
and *.  (They were easy.)  I would like to expand it to include
division, mod/remainder, gcd, sqrt, exp, log, trig functions,
etc.

If anyone would like to help me in this effort, I'd appreciate 
it.  If anyone can supply/recommend algorithms, that would be 
appreciated, too.  Alternate implementations and improvements 
will be accepted, as well.  In short, I am completely open to 
suggestion.  Please help.

I will probably need to stick with a Caml Light implementation 
because the 'C' interface is only available on Unix, and I am 
working on an Amiga.  Besides, this would make the code useable 
by all.

Jerry D. Hedden
hedden@esdsdf.dnet.ge.com

====================== Cut Here ===============================


(* Bignums module *)
(* Functions implemented so far:

        renormalize : bignum -> bignum = <fun>
          Internal -- used to "fixup" the result of a calculation

        add_big : bignum -> bignum -> bignum = <fun>
        sub_big : bignum -> bignum -> bignum = <fun>
        mult_big : bignum -> bignum -> bignum = <fun>
          Basic operations

        string_of_bignum : bignum -> string = <fun>
        bignum_of_string : string -> bignum = <fun>
          Conversion functions

        big_fib : int -> bignum = <fun>
        big_fact : int -> bignum = <fun>
          Example functions that use bignums
*)

type bignum = {Neg:bool; Point:int; Value:int vect};;

let rec renormalize {Neg=neg;Point=pt;Value=v} =
  let vl = vect_length v in
  begin
    for i = (pred vl) downto 1 do
      let x = v.(i) in
      if x >= 10000
         then (v.(i)      <- x mod 10000;
               v.(pred i) <- v.(pred i) + x/10000)
         else
      if x < 0
         then (v.(i)      <- 10000 + (x mod 10000);
               v.(pred i) <- v.(pred i) - (succ (x/10000)))
    done;
    if v.(0) < 0
       then renormalize {Neg=not neg; Point=pt;
                         Value=(map_vect (function a -> -a) v)}
       else let first =
              let ff =
                let rec find_first n =
                  if n < vl
                     then if (eq_int v.(n) 0)
                             then find_first (succ n)
                             else n
                     else n
                in find_first 0
              in min ff pt
            and last =
              let ll =
                let rec find_last n =
                  if n >= 0
                     then if (eq_int v.(n) 0)
                             then find_last (pred n)
                             else n
                     else n
                in find_last (pred vl)
              in max ll pt
            in if first >= vl
                  then {Neg=false;Point=0;Value=[|0|]}
                  else if (eq_int first 0) & (eq_int last (pred vl))
                          then {Neg=neg;Point=pt;Value=v}
                          else let vvl = succ (last-first) in
                               let vv = make_vect vvl 0 in
                               begin
                                 blit_vect v first vv 0 vvl;
                                 {Neg=neg;Point=(pt-first);Value=vv}
                               end
  end
;;


let add_big {Neg=nx;Point=px;Value=vx} {Neg=ny;Point=py;Value=vy} =
  let xl = vect_length vx
  and yl = vect_length vy
  and dpt = succ (max px py) in
  let len = dpt + (max (xl-px) (yl-py)) in
  let xx = make_vect len 0
  and yy = make_vect len 0
  in begin
    blit_vect (if nx then (map_vect (function a -> -a) vx)
                        else vx)
              0 xx (dpt-px) xl;
    blit_vect (if ny then (map_vect (function a -> -a) vy)
                        else vy)
              0 yy (dpt-py) yl;
    renormalize {Neg=false; Point=dpt;
                 Value=vect_of_list (map2 (prefix +) (list_of_vect xx)
                                                     (list_of_vect yy))}
  end
;;


let sub_big {Neg=nx;Point=px;Value=vx} {Neg=ny;Point=py;Value=vy} =
  let xl = vect_length vx
  and yl = vect_length vy
  and dpt = succ (max px py) in
  let len = dpt + (max (xl-px) (yl-py)) in
  let xx = make_vect len 0
  and yy = make_vect len 0
  in begin
    blit_vect (if nx then (map_vect (function a -> -a) vx)
                        else vx)
              0 xx (dpt-px) xl;
    blit_vect (if ny then (map_vect (function a -> -a) vy)
                        else vy)
              0 yy (dpt-py) yl;
    renormalize {Neg=false; Point=dpt;
                  Value=vect_of_list (map2 (prefix -) (list_of_vect xx)
                                                      (list_of_vect yy))}
  end
;;


let mult_big {Neg=nx;Point=px;Value=vx} {Neg=ny;Point=py;Value=vy} =
  let xl = vect_length vx
  and yl = vect_length vy
  in
  let z = make_vect (xl+yl) 0
  in begin
    for j = (yl-1) downto 0 do
      for i = (xl-1) downto 0 do
        z.(i+j+1) <- z.(i+j+1) + (vx.(i) * vy.(j))
      done;
      for i = (xl+j) downto (j+1) do
        let q = z.(i) in
        if q >= 10000
           then (z.(i)      <- q mod 10000;
                 z.(pred i) <- z.(pred i) + q/10000)
      done
    done;
    renormalize {Neg=not(nx=ny);Point=(px+py+1);Value=z}
  end
;;


let string_of_bignum {Neg=neg;Point=pt;Value=v} =
  let vl = vect_length v
  and n4 n =
    let s = string_of_int n in
    (make_string (4-(string_length s)) `0`) ^ s
  in
  let rec no_trail s =
    let ln = string_length s in
    if (eq_int ln 0) then ""
                     else if (nth_char s (pred ln)) = `0`
                             then no_trail (sub_string s 0 (pred ln))
                             else s
  and nstr a b =
    if a<=b then (n4 v.(a)) ^ (nstr (succ a) b)
            else ""
  in
  let sign = if neg then "-" else ""
  and point = if (succ pt) < vl then "." else ""
  and last = if (succ pt) < vl
                then no_trail (n4 v.(pred vl))
                else ""
  in sign ^ (string_of_int v.(0)) ^ (nstr 1 pt) ^
              point ^ (nstr (succ pt) (vl-2)) ^ last
;;


let bignum_of_string s =
  let last = pred (string_length s)
  and neg,first =
    match nth_char s 0 with
        `-` -> true,1
      | `+` -> false,1
      | _   -> false,0
  in
  let dp = find_dp 0
    where rec find_dp n =
      if n > last
         then n
         else if (nth_char s n) = `.`
                 then n
                 else find_dp (succ n)
  in
  let asize = let size = (3+dp-first)/4 in
              if (eq_int size 0) then 1 else size
  and bsize = (3+last-dp)/4
  in
  let x = make_vect (asize+bsize) 0 in
  let rec set_num n y =
    if (n-4) <= first
       then  x.(y) <- int_of_string (sub_string s first (n-first))
       else (x.(y) <- int_of_string (sub_string s (n-4) 4);
             set_num (n-4) (pred y))
  and set_dec n y =
    if (n+3) >= last
       then  x.(y) <- int_of_string (sub_string (s^"000") n 4)
       else (x.(y) <- int_of_string (sub_string s n 4);
             set_dec (n+4) (succ y))
  in
  begin
    (if dp > first then set_num dp (pred asize));
    (if dp < last then set_dec (succ dp) asize);
    {Neg=neg;Point=(pred asize);Value=x}
  end
;;


let big_fib = fib_rec (bignum_of_string "0") (bignum_of_string "1")
  where rec fib_rec a b n = if n<=0 then a else fib_rec b (add_big a b) (pred n)
;;


let big_fact = fact_rec (bignum_of_string "1")
  where rec fact_rec ans n =
    if n<=0
       then ans
       else fact_rec (mult_big ans (bignum_of_string (string_of_int n))) (pred n)
;;