(* int-inf.sml * * COPYRIGHT (c) 1995 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * This package is derived from Andrzej Filinski's bignum package. It is versy * close to the definition of the optional IntInf structure in the SML'97 basis. * * It is implemented almost totally on the abstraction presented by * the BigNat structure. The only concrete type information it assumes * is that BigNat.bignat = 'a list and that BigNat.zero = []. * Some trivial additional efficiency could be obtained by assuming that * type bignat is really int list, and that if (v : bignat) = [d], then * bignat d = [d]. * * At some point, this should be reimplemented to make use of Word32, or * have compiler/runtime support. * * Also, for booting, this module could be broken into one that has * all the types and arithmetic functions, but doesn't use NumScan, * constructing values from strings using bignum arithmetic. Various * integer and word scanning, such as NumScan, could then be constructed * from IntInf. Finally, a user-level IntInf could be built by * importing the basic IntInf, but replacing the scanning functions * by more efficient ones based on the functions in NumScan. * *) structure IntInf :> INT_INF = struct (* It is not clear what advantage there is to having NumFormat as * a submodule. *) structure NumScan : sig val skipWS : (char, 'a) StringCvt.reader -> 'a -> 'a val scanWord : StringCvt.radix -> (char, 'a) StringCvt.reader -> 'a -> (Word32.word * 'a) option val scanInt : StringCvt.radix -> (char, 'a) StringCvt.reader -> 'a -> (int * 'a) option (** should be to int32 **) end = struct structure W = Word32 structure I = Int31 val op < = W.< val op >= = W.>= val op + = W.+ val op - = W.- val op * = W.* val largestWordDiv10 : Word32.word = 0w429496729(* 2^32-1 divided by 10 *) val largestWordMod10 : Word32.word = 0w5 (* remainder *) val largestNegInt : Word32.word = 0w1073741824 (* absolute value of ~2^30 *) val largestPosInt : Word32.word = 0w1073741823 (* 2^30-1 *) type 'a chr_strm = {getc : (char, 'a) StringCvt.reader} (* A table for mapping digits to values. Whitespace characters map to * 128, "+" maps to 129, "-","~" map to 130, "." maps to 131, and the * characters 0-9,A-Z,a-z map to their * base-36 value. All other * characters map to 255. *) local val cvtTable = "\ \\255\255\255\255\255\255\255\255\255\128\128\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\128\255\255\255\255\255\255\255\255\255\255\129\255\130\131\255\ \\000\001\002\003\004\005\006\007\008\009\255\255\255\255\255\255\ \\255\010\011\012\013\014\015\016\017\018\019\020\021\022\023\024\ \\025\026\027\028\029\030\031\032\033\034\035\255\255\255\255\255\ \\255\010\011\012\013\014\015\016\017\018\019\020\021\022\023\024\ \\025\026\027\028\029\030\031\032\033\034\035\255\255\255\130\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ \" val ord = Char.ord in fun code (c : char) = W.fromInt(ord(CharVector.sub(cvtTable, ord c))) val wsCode : Word32.word = 0w128 val plusCode : Word32.word = 0w129 val minusCode : Word32.word = 0w130 end (* local *) fun skipWS (getc : (char, 'a) StringCvt.reader) cs = let fun skip cs = (case (getc cs) of NONE => cs | (SOME(c, cs')) => if (code c = wsCode) then skip cs' else cs (* end case *)) in skip cs end (* skip leading whitespace and any sign (+, -, or ~) *) fun scanPrefix (getc : (char, 'a) StringCvt.reader) cs = let fun skipWS cs = (case (getc cs) of NONE => NONE | (SOME(c, cs')) => let val c' = code c in if (c' = wsCode) then skipWS cs' else SOME(c', cs') end (* end case *)) fun getNext (neg, cs) = (case (getc cs) of NONE => NONE | (SOME(c, cs)) => SOME{neg=neg, next=code c, rest=cs} (* end case *)) in case (skipWS cs) of NONE => NONE | (SOME(c, cs')) => if (c = plusCode) then getNext(false, cs') else if (c = minusCode) then getNext(true, cs') else SOME{neg=false, next=c, rest=cs'} (* end case *) end (* for power of 2 bases (2, 8 & 16), we can check for overflow by looking * at the hi (1, 3 or 4) bits. *) fun chkOverflow mask w = if (W.andb(mask, w) = 0w0) then () else raise Overflow fun scanBin (getc : (char, 'a) StringCvt.reader) cs = (case (scanPrefix getc cs) of NONE => NONE | (SOME{neg, next, rest}) => let fun isDigit (d : Word32.word) = (d < 0w2) val chkOverflow = chkOverflow 0wx80000000 fun cvt (w, rest) = (case (getc rest) of NONE => SOME{neg=neg, word=w, rest=rest} | SOME(c, rest') => let val d = code c in if (isDigit d) then ( chkOverflow w; cvt(W.+(W.<<(w, 0w1), d), rest')) else SOME{neg=neg, word=w, rest=rest} end (* end case *)) in if (isDigit next) then cvt(next, rest) else NONE end (* end case *)) fun scanOct getc cs = (case (scanPrefix getc cs) of NONE => NONE | (SOME{neg, next, rest}) => let fun isDigit (d : Word32.word) = (d < 0w8) val chkOverflow = chkOverflow 0wxE0000000 fun cvt (w, rest) = (case (getc rest) of NONE => SOME{neg=neg, word=w, rest=rest} | SOME(c, rest') => let val d = code c in if (isDigit d) then ( chkOverflow w; cvt(W.+(W.<<(w, 0w3), d), rest')) else SOME{neg=neg, word=w, rest=rest} end (* end case *)) in if (isDigit next) then cvt(next, rest) else NONE end (* end case *)) fun scanDec getc cs = (case (scanPrefix getc cs) of NONE => NONE | (SOME{neg, next, rest}) => let fun isDigit (d : Word32.word) = (d < 0w10) fun cvt (w, rest) = (case (getc rest) of NONE => SOME{neg=neg, word=w, rest=rest} | SOME(c, rest') => let val d = code c in if (isDigit d) then ( if ((w >= largestWordDiv10) andalso ((largestWordDiv10 < w) orelse (largestWordMod10 < d))) then raise Overflow else (); cvt (0w10*w+d, rest')) else SOME{neg=neg, word=w, rest=rest} end (* end case *)) in if (isDigit next) then cvt(next, rest) else NONE end (* end case *)) fun scanHex getc cs = (case (scanPrefix getc cs) of NONE => NONE | (SOME{neg, next, rest}) => let fun isDigit (d : Word32.word) = (d < 0w16) val chkOverflow = chkOverflow 0wxF0000000 fun cvt (w, rest) = (case (getc rest) of NONE => SOME{neg=neg, word=w, rest=rest} | SOME(c, rest') => let val d = code c in if (isDigit d) then ( chkOverflow w; cvt(W.+(W.<<(w, 0w4), d), rest')) else SOME{neg=neg, word=w, rest=rest} end (* end case *)) in if (isDigit next) then cvt(next, rest) else NONE end (* end case *)) fun finalWord scanFn getc cs = (case (scanFn getc cs) of NONE => NONE | (SOME{neg=true, ...}) => NONE | (SOME{neg=false, word, rest}) => SOME(word, rest) (* end case *)) fun scanWord StringCvt.BIN = finalWord scanBin | scanWord StringCvt.OCT = finalWord scanOct | scanWord StringCvt.DEC = finalWord scanDec | scanWord StringCvt.HEX = finalWord scanHex fun finalInt scanFn getc cs = (case (scanFn getc cs) of NONE => NONE | (SOME{neg=true, word, rest}) => if (largestNegInt < word) then raise Overflow else SOME(I.~(W.toInt word), rest) | (SOME{word, rest, ...}) => if (largestPosInt < word) then raise Overflow else SOME(W.toInt word, rest) (* end case *)) fun scanInt StringCvt.BIN = finalInt scanBin | scanInt StringCvt.OCT = finalInt scanOct | scanInt StringCvt.DEC = finalInt scanDec | scanInt StringCvt.HEX = finalInt scanHex end (* structure NumScan *) structure NumFormat : sig val fmtWord : StringCvt.radix -> Word32.word -> string val fmtInt : StringCvt.radix -> int -> string (** should be int32 **) end = struct structure W = Word32 structure I = Int val op < = W.< val op - = W.- val op * = W.* val op div = W.div fun mkDigit (w : Word32.word) = CharVector.sub("0123456789abcdef", W.toInt w) fun wordToBin w = let fun mkBit w = if (W.andb(w, 0w1) = 0w0) then #"0" else #"1" fun f (0w0, n, l) = (I.+(n, 1), #"0" :: l) | f (0w1, n, l) = (I.+(n, 1), #"1" :: l) | f (w, n, l) = f(W.>>(w, 0w1), I.+(n, 1), (mkBit w) :: l) in f (w, 0, []) end fun wordToOct w = let fun f (w, n, l) = if (w < 0w8) then (I.+(n, 1), (mkDigit w) :: l) else f(W.>>(w, 0w3), I.+(n, 1), mkDigit(W.andb(w, 0w7)) :: l) in f (w, 0, []) end fun wordToDec w = let fun f (w, n, l) = if (w < 0w10) then (I.+(n, 1), (mkDigit w) :: l) else let val j = w div 0w10 in f (j, I.+(n, 1), mkDigit(w - 0w10*j) :: l) end in f (w, 0, []) end fun wordToHex w = let fun f (w, n, l) = if (w < 0w16) then (I.+(n, 1), (mkDigit w) :: l) else f(W.>>(w, 0w4), I.+(n, 1), mkDigit(W.andb(w, 0w15)) :: l) in f (w, 0, []) end fun fmtW StringCvt.BIN = #2 o wordToBin | fmtW StringCvt.OCT = #2 o wordToOct | fmtW StringCvt.DEC = #2 o wordToDec | fmtW StringCvt.HEX = #2 o wordToHex fun fmtWord radix = String.implode o (fmtW radix) (** NOTE: this currently uses 31-bit integers, but really should use 32-bit ** ints (once they are supported). **) fun fmtInt radix = let val fmtW = fmtW radix val itow = W.fromInt fun fmt i = if I.<(i, 0) then let val (digits) = fmtW(itow(I.~ i)) in String.implode(#"~"::digits) end handle _ => (case radix of StringCvt.BIN => "~1111111111111111111111111111111" | StringCvt.OCT => "~7777777777" | StringCvt.DEC => "~1073741824" | StringCvt.HEX => "~3fffffff" (* end case *)) else String.implode(fmtW(itow i)) in fmt end end (* structure NumFormat *) structure BigNat = struct exception Negative val itow = Word.fromInt val wtoi = Word.toIntX val lgBase = 30 (* No. of bits per digit; must be even *) val nbase = ~0x40000000 (* = ~2^lgBase *) val maxDigit = ~(nbase + 1) val realBase = (real maxDigit) + 1.0 val lgHBase = Int.quot (lgBase, 2) (* half digits *) val hbase = Word.<<(0w1, itow lgHBase) val hmask = hbase-0w1 fun quotrem (i, j) = (Int.quot (i, j), Int.rem (i, j)) fun scale i = if i = maxDigit then 1 else nbase div (~(i+1)) type bignat = int list (* least significant digit first *) val zero = [] val one = [1] fun bignat 0 = zero | bignat i = let val notNbase = Word.notb(itow nbase) fun bn 0w0 = [] | bn i = let fun dmbase n = (Word.>> (n, itow lgBase), Word.andb (n, notNbase)) val (q,r) = dmbase i in (wtoi r)::(bn q) end in if i > 0 then if i <= maxDigit then [i] else bn (itow i) else raise Negative end fun int [] = 0 | int [d] = d | int [d,e] = ~(nbase*e) + d | int (d::r) = ~(nbase*int r) + d fun consd (0, []) = [] | consd (d, r) = d::r fun hl i = let val w = itow i in (wtoi(Word.~>> (w, itow lgHBase)), (* MUST sign-extend *) wtoi(Word.andb(w, hmask))) end fun sh i = wtoi(Word.<< (itow i, itow lgHBase)) fun addOne [] = [1] | addOne (m::rm) = let val c = nbase+m+1 in if c < 0 then (c-nbase)::rm else c::(addOne rm) end fun add ([], digits) = digits | add (digits, []) = digits | add (dm::rm, dn::rn) = addd (nbase+dm+dn, rm, rn) and addd (s, m, n) = if s < 0 then (s-nbase) :: add (m, n) else (s :: addc (m, n)) and addc (m, []) = addOne m | addc ([], n) = addOne n | addc (dm::rm, dn::rn) = addd (nbase+dm+dn+1, rm, rn) fun subtOne (0::mr) = maxDigit::(subtOne mr) | subtOne [1] = [] | subtOne (n::mr) = (n-1)::mr | subtOne [] = raise Fail "" fun subt (m, []) = m | subt ([], n) = raise Negative | subt (dm::rm, dn::rn) = subd(dm-dn,rm,rn) and subb ([], n) = raise Negative | subb (dm::rm, []) = subd (dm-1, rm, []) | subb (dm::rm, dn::rn) = subd (dm-dn-1, rm, rn) and subd (d, m, n) = if d >= 0 then consd(d, subt (m, n)) else consd(d-nbase, subb (m, n)) (* multiply 2 digits *) fun mul2 (m, n) = let val (mh, ml) = hl m val (nh, nl) = hl n val x = mh*nh val y = (mh-ml)*(nh-nl) (* x-y+z = mh*nl + ml*nh *) val z = ml*nl val (zh, zl) = hl z val (uh,ul) = hl (nbase+x+z-y+zh) (* can't overflow *) in (x+uh+wtoi hbase, sh ul+zl) end (* multiply bigint by digit *) fun muld (m, 0) = [] | muld (m, 1) = m (* speedup *) | muld (m, i) = let fun muldc ([], 0) = [] | muldc ([], c) = [c] | muldc (d::r, c) = let val (h, l) = mul2 (d, i) val l1 = l+nbase+c in if l1 >= 0 then l1::muldc (r, h+1) else (l1-nbase)::muldc (r, h) end in muldc (m, 0) end fun mult (m, []) = [] | mult (m, [d]) = muld (m, d) (* speedup *) | mult (m, 0::r) = consd (0, mult (m, r)) (* speedup *) | mult (m, n) = let fun muln [] = [] | muln (d::r) = add (muld (n, d), consd (0, muln r)) in muln m end (* divide DP number by digit; assumes u < i , i >= base/2 *) fun divmod2 ((u,v), i) = let val (vh,vl) = hl v val (ih,il) = hl i fun adj (q,r) = if r<0 then adj (q-1, r+i) else (q, r) val (q1,r1) = quotrem (u, ih) val (q1,r1) = adj (q1, sh r1+vh-q1*il) val (q0,r0) = quotrem (r1, ih) val (q0,r0) = adj (q0, sh r0+vl-q0*il) in (sh q1+q0, r0) end (* divide bignat by digit>0 *) fun divmodd (m, 1) = (m, 0) (* speedup *) | divmodd (m, i) = let val scale = scale i val i' = i * scale val m' = muld (m, scale) fun dmi [] = ([], 0) | dmi (d::r) = let val (qt,rm) = dmi r val (q1,r1) = divmod2 ((rm,d), i') in (consd (q1,qt), r1) end val (q,r) = dmi m' in (q, r div scale) end (* From Knuth Vol II, 4.3.1, but without opt. in step D3 *) fun divmod (m, []) = raise Div | divmod ([], n) = ([], []) (* speedup *) | divmod (d::r, 0::s) = let val (qt,rm) = divmod (r,s) in (qt, consd (d, rm)) end (* speedup *) | divmod (m, [d]) = let val (qt, rm) = divmodd (m, d) in (qt, if rm=0 then [] else [rm]) end | divmod (m, n) = let val ln = length n (* >= 2 *) val scale = scale(List.nth (n,ln-1)) val m' = muld (m, scale) val n' = muld (n, scale) val n1 = List.nth (n', ln-1) (* >= base/2 *) fun divl [] = ([], []) | divl (d::r) = let val (qt,rm) = divl r val m = consd (d, rm) fun msds ([],_) = (0,0) | msds ([d],1) = (0,d) | msds ([d2,d1],1) = (d1,d2) | msds (d::r,i) = msds (r,i-1) val (m1,m2) = msds (m, ln) val tq = if m1 = n1 then maxDigit else #1 (divmod2 ((m1,m2), n1)) fun try (q,qn') = (q, subt (m,qn')) handle Negative => try (q-1, subt (qn', n')) val (q,rr) = try (tq, muld (n',tq)) in (consd (q,qt), rr) end val (qt,rm') = divl m' val (rm,_(*0*)) = divmodd (rm',scale) in (qt,rm) end fun cmp ([],[]) = EQUAL | cmp (_,[]) = GREATER | cmp ([],_) = LESS | cmp ((i : int)::ri,j::rj) = case cmp (ri,rj) of EQUAL => if i = j then EQUAL else if i < j then LESS else GREATER | c => c fun exp (_, 0) = one | exp ([], n) = if n > 0 then zero else raise Div | exp (m, n) = if n < 0 then zero else let fun expm 0 = [1] | expm 1 = m | expm i = let val r = expm (i div 2) val r2 = mult (r,r) in if i mod 2 = 0 then r2 else mult (r2, m) end in expm n end local fun try n = if n >= lgHBase then n else try (2*n) val pow2lgHBase = try 1 in fun log2 [] = raise Domain | log2 (h::t) = let fun qlog (x,0) = 0 | qlog (x,b) = if x >= wtoi(Word.<< (0w1, itow b)) then b+qlog (wtoi(Word.>> (itow x, itow b)), b div 2) else qlog (x, b div 2) fun loop (d,[],lg) = lg + qlog (d,pow2lgHBase) | loop (_,h::t,lg) = loop (h,t,lg + lgBase) in loop (h,t,0) end end (* local *) (* find maximal maxpow s.t. radix^maxpow < base * basepow = radix^maxpow *) fun mkPowers radix = let val powers = let val bnd = Int.quot (nbase, (~radix)) fun try (tp,l) = (if tp <= bnd then try (radix*tp,tp::l) else (tp::l)) handle _ => tp::l in Vector.fromList(rev(try (radix,[1]))) end val maxpow = Vector.length powers - 1 in (maxpow, Vector.sub(powers,maxpow), powers) end val powers2 = mkPowers 2 val powers8 = mkPowers 8 val powers10 = mkPowers 10 val powers16 = mkPowers 16 fun fmt (pow, radpow, puti) n = let val pad = StringCvt.padLeft #"0" pow fun ms0 (0,a) = (pad "")::a | ms0 (i,a) = (pad (puti i))::a fun ml (n,a) = case divmodd (n, radpow) of ([],d) => (puti d)::a | (q,d) => ml (q, ms0 (d, a)) in concat (ml (n,[])) end val fmt2 = fmt (#1 powers2, #2 powers2, NumFormat.fmtInt StringCvt.BIN) val fmt8 = fmt (#1 powers8, #2 powers8, NumFormat.fmtInt StringCvt.OCT) val fmt10 = fmt (#1 powers10, #2 powers10, NumFormat.fmtInt StringCvt.DEC) val fmt16 = fmt (#1 powers16, #2 powers16, NumFormat.fmtInt StringCvt.HEX) fun scan (bound,powers,geti) getc cs = let fun get (l,cs) = if l = bound then NONE else case getc cs of NONE => NONE | SOME(c,cs') => SOME(c, (l+1,cs')) fun loop (acc,cs) = case geti get (0,cs) of NONE => (acc,cs) | SOME(0,(sh,cs')) => loop(add(muld(acc,Vector.sub(powers,sh)),[]),cs') | SOME(i,(sh,cs')) => loop(add(muld(acc,Vector.sub(powers,sh)),[i]),cs') in case geti get (0,cs) of NONE => NONE | SOME(0,(_,cs')) => SOME (loop([],cs')) | SOME(i,(_,cs')) => SOME (loop([i],cs')) end fun scan2 getc = scan(#1 powers2, #3 powers2, NumScan.scanInt StringCvt.BIN) getc fun scan8 getc = scan(#1 powers8, #3 powers8, NumScan.scanInt StringCvt.OCT) getc fun scan10 getc = scan(#1 powers10, #3 powers10, NumScan.scanInt StringCvt.DEC) getc fun scan16 getc = scan(#1 powers16, #3 powers16, NumScan.scanInt StringCvt.HEX) getc end (* structure BigNat *) structure BN = BigNat datatype sign = POS | NEG datatype int = BI of { sign : sign, digits : BN.bignat } val zero = BI{sign=POS, digits=BN.zero} val one = BI{sign=POS, digits=BN.one} val minus_one = BI{sign=NEG, digits=BN.one} fun posi digits = BI{sign=POS, digits=digits} fun negi digits = BI{sign=NEG, digits=digits} fun zneg [] = zero | zneg digits = BI{sign=NEG, digits=digits} local val minNeg = valOf Int.minInt val bigNatMinNeg = BN.addOne (BN.bignat (~(minNeg+1))) val bigIntMinNeg = negi bigNatMinNeg in fun toInt (BI{digits=[], ...}) = 0 | toInt (BI{sign=POS, digits}) = BN.int digits | toInt (BI{sign=NEG, digits}) = (~(BN.int digits)) handle _ => if digits = bigNatMinNeg then minNeg else raise Overflow fun fromInt 0 = zero | fromInt i = if i < 0 then if (i = minNeg) then bigIntMinNeg else BI{sign=NEG, digits= BN.bignat (~i)} else BI{sign=POS, digits= BN.bignat i} end (* local *) (* The following assumes LargeInt = Int32. * If IntInf is provided, it will be LargeInt and toLarge and fromLarge * will be the identity function. *) local val minNeg = valOf LargeInt.minInt val maxDigit = LargeInt.fromInt BN.maxDigit val nbase = LargeInt.fromInt BN.nbase val lgBase = Word.fromInt BN.lgBase val notNbase = Word32.notb(Word32.fromInt BN.nbase) fun largeNat (0 : LargeInt.int) = [] | largeNat i = let fun bn (0w0 : Word32.word) = [] | bn i = let fun dmbase n = (Word32.>> (n, lgBase), Word32.andb (n, notNbase)) val (q,r) = dmbase i in (Word32.toInt r)::(bn q) end in if i <= maxDigit then [LargeInt.toInt i] else bn (Word32.fromLargeInt i) end fun large [] = 0 | large [d] = LargeInt.fromInt d | large [d,e] = ~(nbase*(LargeInt.fromInt e)) + (LargeInt.fromInt d) | large (d::r) = ~(nbase*large r) + (LargeInt.fromInt d) val bigNatMinNeg = BN.addOne (largeNat (~(minNeg+1))) val bigIntMinNeg = negi bigNatMinNeg in fun toLarge (BI{digits=[], ...}) = 0 | toLarge (BI{sign=POS, digits}) = large digits | toLarge (BI{sign=NEG, digits}) = (~(large digits)) handle _ => if digits = bigNatMinNeg then minNeg else raise Overflow fun fromLarge 0 = zero | fromLarge i = if i < 0 then if (i = minNeg) then bigIntMinNeg else BI{sign=NEG, digits= largeNat (~i)} else BI{sign=POS, digits= largeNat i} end (* local *) fun negSign POS = NEG | negSign NEG = POS fun subtNat (m, []) = {sign=POS, digits=m} | subtNat ([], n) = {sign=NEG, digits=n} | subtNat (m,n) = ({sign=POS,digits = BN.subt(m,n)}) handle BN.Negative => ({sign=NEG,digits = BN.subt(n,m)}) val precision = NONE val minInt = NONE val maxInt = NONE fun ~ (i as BI{digits=[], ...}) = i | ~ (BI{sign=POS, digits}) = BI{sign=NEG, digits=digits} | ~ (BI{sign=NEG, digits}) = BI{sign=POS, digits=digits} fun op * (_,BI{digits=[], ...}) = zero | op * (BI{digits=[], ...},_) = zero | op * (BI{sign=POS, digits=d1}, BI{sign=NEG, digits=d2}) = BI{sign=NEG,digits=BN.mult(d1,d2)} | op * (BI{sign=NEG, digits=d1}, BI{sign=POS, digits=d2}) = BI{sign=NEG,digits=BN.mult(d1,d2)} | op * (BI{digits=d1,...}, BI{digits=d2,...}) = BI{sign=POS,digits=BN.mult(d1,d2)} fun op + (BI{digits=[], ...}, i2) = i2 | op + (i1, BI{digits=[], ...}) = i1 | op + (BI{sign=POS, digits=d1}, BI{sign=NEG, digits=d2}) = BI(subtNat(d1, d2)) | op + (BI{sign=NEG, digits=d1}, BI{sign=POS, digits=d2}) = BI(subtNat(d2, d1)) | op + (BI{sign, digits=d1}, BI{digits=d2, ...}) = BI{sign=sign, digits=BN.add(d1, d2)} fun op - (i1, BI{digits=[], ...}) = i1 | op - (BI{digits=[], ...}, BI{sign, digits}) = BI{sign=negSign sign, digits=digits} | op - (BI{sign=POS, digits=d1}, BI{sign=POS, digits=d2}) = BI(subtNat(d1, d2)) | op - (BI{sign=NEG, digits=d1}, BI{sign=NEG, digits=d2}) = BI(subtNat(d2, d1)) | op - (BI{sign, digits=d1}, BI{digits=d2, ...}) = BI{sign=sign, digits=BN.add(d1, d2)} fun quotrem (BI{sign=POS,digits=m},BI{sign=POS,digits=n}) = (case BN.divmod (m,n) of (q,r) => (posi q, posi r)) | quotrem (BI{sign=POS,digits=m},BI{sign=NEG,digits=n}) = (case BN.divmod (m,n) of (q,r) => (zneg q, posi r)) | quotrem (BI{sign=NEG,digits=m},BI{sign=POS,digits=n}) = (case BN.divmod (m,n) of (q,r) => (zneg q, zneg r)) | quotrem (BI{sign=NEG,digits=m},BI{sign=NEG,digits=n}) = (case BN.divmod (m,n) of (q,r) => (posi q, zneg r)) fun divmod (BI{sign=POS,digits=m},BI{sign=POS,digits=n}) = (case BN.divmod (m,n) of (q,r) => (posi q, posi r)) | divmod (BI{sign=POS,digits=[]},BI{sign=NEG,digits=n}) = (zero,zero) | divmod (BI{sign=POS,digits=m},BI{sign=NEG,digits=n}) = let val (q,r) = BN.divmod (BN.subtOne m, n) in (negi(BN.addOne q), zneg(BN.subtOne(BN.subt(n,r)))) end | divmod (BI{sign=NEG,digits=m},BI{sign=POS,digits=n}) = let val (q,r) = BN.divmod (BN.subtOne m, n) in (negi(BN.addOne q), posi(BN.subtOne(BN.subt(n,r)))) end | divmod (BI{sign=NEG,digits=m},BI{sign=NEG,digits=n}) = (case BN.divmod (m,n) of (q,r) => (posi q, zneg r)) fun op div arg = #1(divmod arg) fun op mod arg = #2(divmod arg) fun op quot arg = #1(quotrem arg) fun op rem arg = #2(quotrem arg) fun compare (BI{sign=NEG,...},BI{sign=POS,...}) = LESS | compare (BI{sign=POS,...},BI{sign=NEG,...}) = GREATER | compare (BI{sign=POS,digits=d},BI{sign=POS,digits=d'}) = BN.cmp (d,d') | compare (BI{sign=NEG,digits=d},BI{sign=NEG,digits=d'}) = BN.cmp (d',d) fun op < arg = case compare arg of LESS => true | _ => false fun op > arg = case compare arg of GREATER => true | _ => false fun op <= arg = case compare arg of GREATER => false | _ => true fun op >= arg = case compare arg of LESS => false | _ => true fun abs (BI{sign=NEG, digits}) = BI{sign=POS, digits=digits} | abs i = i fun max arg = case compare arg of GREATER => #1 arg | _ => #2 arg fun min arg = case compare arg of LESS => #1 arg | _ => #2 arg fun sign (BI{sign=NEG,...}) = ~1 | sign (BI{digits=[],...}) = 0 | sign _ = 1 fun sameSign (i,j) = sign i = sign j local fun fmt' fmtFn i = case i of (BI{digits=[],...}) => "0" | (BI{sign=NEG,digits}) => "~"^(fmtFn digits) | (BI{sign=POS,digits}) => fmtFn digits in fun fmt StringCvt.BIN = fmt' (BN.fmt2) | fmt StringCvt.OCT = fmt' (BN.fmt8) | fmt StringCvt.DEC = fmt' (BN.fmt10) | fmt StringCvt.HEX = fmt' (BN.fmt16) end val toString = fmt StringCvt.DEC local fun scan' scanFn getc cs = let val cs' = NumScan.skipWS getc cs fun cvt (NONE,_) = NONE | cvt (SOME(i,cs),wr) = SOME(wr i, cs) in case (getc cs') of (SOME(#"~", cs'')) => cvt(scanFn getc cs'',zneg) | (SOME(#"-", cs'')) => cvt(scanFn getc cs'',zneg) | (SOME(#"+", cs'')) => cvt(scanFn getc cs'',posi) | (SOME _) => cvt(scanFn getc cs',posi) | NONE => NONE (* end case *) end in fun scan StringCvt.BIN = scan' (BN.scan2) | scan StringCvt.OCT = scan' (BN.scan8) | scan StringCvt.DEC = scan' (BN.scan10) | scan StringCvt.HEX = scan' (BN.scan16) end fun fromString s = StringCvt.scanString (scan StringCvt.DEC) s fun pow (_, 0) = one | pow (BI{sign=POS,digits}, n) = posi(BN.exp(digits,n)) | pow (BI{sign=NEG,digits}, n) = if Int.mod (n, 2) = 0 then posi(BN.exp(digits,n)) else zneg(BN.exp(digits,n)) fun log2 (BI{sign=POS,digits}) = BN.log2 digits | log2 _ = raise Domain end (* structure IntInf *)