)abbrev category INS IntegerNumberSystem
++ Author: Stephen M. Watt
++ Date Created: January 1988
++ Basic Operations:
++   addmod, base, bit?, copy, dec, even?, hash, inc, invmod, length, mask,
++   positiveRemainder, symmetricRemainder, multiplicativeValuation, mulmod,
++   odd?, powmod, random, rational, rational?, rationalIfCan, shift, submod
++ Description:  An \spad{IntegerNumberSystem} is a model for the integers.
IntegerNumberSystem() : Category ==
  Join(UniqueFactorizationDomain, EuclideanDomain, OrderedIntegralDomain,
       DifferentialRing, ConvertibleTo Integer, RetractableTo Integer,
       ConvertibleTo InputForm,
       ConvertibleTo Pattern Integer, PatternMatchable Integer,
       CombinatorialFunctionCategory, RealConstant,
       CharacteristicZero, StepThrough, canonicalUnitNormal,
       multiplicativeValuation) with
   odd?     : % -> Boolean
      ++ odd?(n) returns true if and only if n is odd.
   even?    : % -> Boolean
      ++ even?(n) returns true if and only if n is even.
   base     : () -> %
      ++ base() returns the base for the operations of \spad{IntegerNumberSystem}.
   length   : % -> %
      ++ length(a) length of \spad{a} in digits.
   shift    : (%, %) -> %
      ++ shift(a, i) shift \spad{a} by i digits.
   bit?     : (%, %) -> Boolean
      ++ bit?(n, i) returns true if and only if i-th bit of n is a 1.
   positiveRemainder     : (%, %) -> %
      ++ positiveRemainder(a, b) (where \spad{b > 1}) yields r
      ++ where \spad{0 <= r < b} and \spad{r = a rem b}.
   symmetricRemainder     : (%, %) -> %
      ++ symmetricRemainder(a, b) (where \spad{b > 1}) yields r
      ++ where \spad{ -b/2 <= r < b/2 }.
   rational? : % -> Boolean
      ++ rational?(n) tests if n is a rational number
      ++ (see \spadtype{Fraction Integer}).
   rational : % -> Fraction Integer
      ++ rational(n) creates a rational number (see \spadtype{Fraction Integer}).
   rationalIfCan : % -> Union(Fraction Integer, "failed")
      ++ rationalIfCan(n) creates a rational number, or returns "failed" if this is not possible.
   random   : % -> %
      ++ random(n) creates a random element from 0 to \spad{n-1}.
   copy     : % -> %
      ++ copy(n) gives a copy of n.
   inc      : % -> %
      ++ inc(x) returns \spad{x + 1}.
   dec      : % -> %
      ++ dec(x) returns \spad{x - 1}.
   mask     : % -> %
      ++ mask(n) returns \spad{2^n-1} (an n bit mask).
   addmod   : (%, %, %) -> %
      ++ addmod(a, b, p), \spad{0<=a, b<p>1}, means \spad{a+b mod p}.
   submod   : (%, %, %) -> %
      ++ submod(a, b, p), \spad{0<=a, b<p>1}, means \spad{a-b mod p}.
   mulmod   : (%, %, %) -> %
      ++ mulmod(a, b, p), \spad{0<=a, b<p>1}, means \spad{a*b mod p}.
   powmod   : (%, %, %) -> %
      ++ powmod(a, b, p), \spad{0<=a, b<p>1}, means \spad{a^b mod p}.
   invmod   : (%, %) -> %
      ++ invmod(a, b), \spad{0<=a<b>1}, \spad{(a, b)=1} means \spad{1/a mod b}.

 add
   characteristic()         == 0
   differentiate x          == 0
   even? x                  == not odd? x
   positive? x              == x > 0
   copy x                   == x
   bit?(x, i)               == odd? shift(x, -i)
   mask n                   == dec shift(1, n)
   rational? x              == true
   euclideanSize(x)         ==
        x = 0 => error "euclideanSize called on zero"
        x<0 => (-convert(x)@Integer)::NonNegativeInteger
        convert(x)@Integer::NonNegativeInteger
   convert(x : %) : Float       == (convert(x)@Integer)::Float
   convert(x : %) : DoubleFloat  == (convert(x)@Integer)::DoubleFloat
   convert(x : %) : InputForm   == convert(convert(x)@Integer)
   retract(x : %) : Integer     == convert(x)@Integer
   convert(x : %) : Pattern(Integer)== convert(x)@Integer ::Pattern(Integer)
   factor x          == factor(x)$IntegerFactorizationPackage(%)
   squareFree x      == squareFree(x)$IntegerFactorizationPackage(%)
   prime? x          == prime?(x)$IntegerPrimesPackage(%)
   factorial x       == factorial(x)$IntegerCombinatoricFunctions(%)
   binomial(n, m)    == binomial(n, m)$IntegerCombinatoricFunctions(%)
   permutation(n, m) == permutation(n, m)$IntegerCombinatoricFunctions(%)
   retractIfCan(x:%):Union(Integer, "failed") == convert(x)@Integer

   init() == 0

   -- iterates in order 0, 1, -1, 2, -2, 3, -3, ...
   nextItem(n) ==
     zero? n => 1
     n>0 => -n
     1-n

   patternMatch(x, p, l) ==
     patternMatch(x, p, l)$PatternMatchIntegerNumberSystem(%)

   rational(x : %) : Fraction(Integer) ==
     (convert(x)@Integer)::Fraction(Integer)

   rationalIfCan(x:%):Union(Fraction Integer, "failed") ==
     (convert(x)@Integer)::Fraction(Integer)

   symmetricRemainder(x, n) ==
      r := x rem n
      r = 0 => r
      if n < 0 then n := -n
      r > 0 =>
         2 * r > n => r - n
         r
      2*r + n <= 0 => r + n
      r

   invmod(a, b) ==
      if negative? a then a := positiveRemainder(a, b)
      c := a; c1 : % := 1
      d := b; d1 : % := 0
      while not zero? d repeat
         q := c quo d
         r := c-q*d
         r1 := c1-q*d1
         c := d; c1 := d1
         d := r; d1 := r1
      not (c = 1) => error "inverse does not exist"
      negative? c1 => c1 + b
      c1

   powmod(x, n, p) ==
      if negative? x then x := positiveRemainder(x, p)
      zero? x => 0
      zero? n => 1
      y : % := 1
      z := x
      repeat
         if odd? n then y := mulmod(y, z, p)
         zero?(n := shift(n, -1)) => return y
         z := mulmod(z, z, p)


)abbrev domain SINT SingleInteger

++ Author:  Michael Monagan
++ Date Created: January 1988
++ Basic Operations: max, min,
++    not, and, or, xor, Not, And, Or
++ Related Constructors:
++ Keywords: single integer
++ Description: SingleInteger is intended to support machine integer
++ arithmetic.

-- MAXINT  (machine integer constants)


-- Lisp dependencies
-- EQ, ABSVAL, TIMES, INTEGER-LENGTH, HASHEQ, REMAINDER
-- QSLESSP, QSGREATERP, QSADD1, QSSUB1, QSMINUS, QSPLUS, QSDIFFERENCE
-- QSTIMES, QSREMAINDER, QSODDP, QSZEROP, QSMAX, QSMIN, QSNOT, QSAND
-- QSOR, QSXOR, QSLEFTSHIFT, QSADDMOD, QSDIFMOD, QSMULTMOD


SingleInteger() : Join(IntegerNumberSystem, ConvertibleTo String,
       Logic, OpenMath, Canonical, canonicalsClosed) with

   qconvert : Integer -> %
      ++ qconvert(x) converts x to % trusting that x is
      ++ in correct range.
   max      : () -> %
      ++ max() returns the largest single integer.
   min      : () -> %
      ++ min() returns the smallest single integer.

   -- bit operations
   "not":   % -> %
      ++ not(n) returns the bit-by-bit logical {\em not} of the single integer n.
   "~"  :   % -> %
      ++  ~ n returns the bit-by-bit logical {\em not} of the single integer n.
   "/\": (%, %) -> %
      ++ n /\ m  returns the bit-by-bit logical {\em and} of
      ++ the single integers n and m.
   "\/" : (%, %) -> %
      ++ n \/ m  returns the bit-by-bit logical {\em or} of
      ++ the single integers n and m.
   "xor": (%, %) -> %
      ++ xor(n, m)  returns the bit-by-bit logical {\em xor} of
      ++ the single integers n and m.
   Not  : % -> %
      ++ Not(n) returns the bit-by-bit logical {\em not} of the single integer n.
   And  : (%, %) -> %
      ++ And(n, m)  returns the bit-by-bit logical {\em and} of
      ++ the single integers n and m.
   Or   : (%, %) -> %
      ++ Or(n, m)  returns the bit-by-bit logical {\em or} of
      ++ the single integers n and m.

 == add

   MAXINT ==> MOST_-POSITIVE_-FIXNUM$Lisp
   MININT ==> MOST_-NEGATIVE_-FIXNUM$Lisp

   writeOMSingleInt(dev : OpenMathDevice, x : %) : Void ==
    if x < 0 then
      OMputApp(dev)
      OMputSymbol(dev, "arith1", "unary_minus")
      OMputInteger(dev, convert(-x))
      OMputEndApp(dev)
    else
      OMputInteger(dev, convert(x))

   OMwrite(dev : OpenMathDevice, x : %, wholeObj : Boolean) : Void ==
    if wholeObj then
      OMputObject(dev)
    writeOMSingleInt(dev, x)
    if wholeObj then
      OMputEndObject(dev)

   coerce(x) : OutputForm == (convert(x)@Integer)::OutputForm
   convert(x : %) : Integer == x pretend Integer
   convert(x : %) : String == string(convert(x)@Integer)
   i : Integer * y : %      == i::% * y
   0         == 0$Lisp
   1         == 1$Lisp
   base()    == 2$Lisp
   max()     == MAXINT
   min()     == MININT
   x = y     == eql_SI(x, y)$Lisp
   _~ x      == not_SI(x)$Lisp
   not(x)    == not_SI(x)$Lisp
   _/_\(x, y) == and_SI(x, y)$Lisp
   _\_/(x, y) == or_SI(x, y)$Lisp
   Not(x)    == not_SI(x)$Lisp
   And(x, y)  == and_SI(x, y)$Lisp
   Or(x, y)   == or_SI(x, y)$Lisp
   xor(x, y)  == xor_SI(x, y)$Lisp
   x < y     == less_SI(x, y)$Lisp
   inc x     == inc_SI(x)$Lisp
   dec x     == dec_SI(x)$Lisp
   - x       == minus_SI(x)$Lisp
   x + y     == add_SI(x, y)$Lisp
   x : % - y : % == sub_SI(x, y)$Lisp
   x : % * y : % == mul_SI(x, y)$Lisp
   x : % ^ n : NonNegativeInteger == ((EXPT(x, n)$Lisp) pretend Integer)::%
   x quo y   == quo_SI(x, y)$Lisp
   x rem y   == rem_SI(x, y)$Lisp
   divide(x, y)   == CONS(quo_SI(x, y)$Lisp, rem_SI(x, y)$Lisp)$Lisp
   gcd(x, y)  == GCD(x, y)$Lisp
   abs(x)    == abs_SI(x)$Lisp
   odd?(x)   == odd?_SI(x)$Lisp
   zero?(x)  == zero?_SI(x)$Lisp
   one?(x)   == x = 1
   max(x, y)  == max_SI(x, y)$Lisp
   min(x, y)  == min_SI(x, y)$Lisp
   hashUpdate!(hs, s) == update!(hs, SXHASH(s)$Lisp)$HashState
   length(x) == INTEGER_-LENGTH(x)$Lisp
   shift(x, n)    == lshift_SI(x, n)$Lisp
   mulmod(a, b, p) == mulmod_SI(a, b, p)$Lisp
   addmod(a, b, p) == addmod_SI(a, b, p)$Lisp
   submod(a, b, p) == submod_SI(a, b, p)$Lisp
   negative?(x)  == negative?_SI$Lisp x


   positiveRemainder(x, n) ==
      r : % := rem_SI(x, n)$Lisp
      negative?_SI(r)$Lisp =>
          negative?_SI(n)$Lisp => sub_SI(x, n)$Lisp
          add_SI(r, n)$Lisp
      r

   qconvert(x : Integer) : % == x pretend %

   coerce(x : Integer) : % ==
      (x <= max pretend Integer) and (x >= min pretend Integer) =>
        x pretend %
      error "integer too large to represent in a machine word"

   random(n) == RANDOM(n)$Lisp

   UCA ==> Record(unit : %, canonical : %, associate : %)
   unitNormal x ==
      x < 0 => [-1, -x, -1]$UCA
      [1, x, 1]$UCA


--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
