pro checksum32, array, checksum, FROM_IEEE = from_IEEE, NOSAVE = nosave, incremental = incremental
;+
; NAME:
;       CHECKSUM32
;
; PURPOSE:
;       To compute the 32bit checksum of an array (ones-complement arithmetic)
;
; EXPLANATION:
;       The 32bit checksum is adopted in the FITS Checksum convention
;       http://fits.gsfc.nasa.gov/registry/checksum.html
;
; CALLING SEQUENCE:
;       CHECKSUM32, array, checksum, [/FROM_IEEE, /NoSAVE, /Incremental]
;
; INPUTS:
;       array - any numeric idl array.  If the number of bytes in the array is 
;               not a multiple of four then it is padded with zeros internally
;               (the array is returned unchanged).   Convert a string array 
;               (e.g. a FITS header) to bytes prior to calling CHECKSUM32.
;
; INPUT-OUTPUT:
;       checksum - unsigned long scalar, giving sum of array elements using 
;                ones-complement arithmetic.    This is normally an output
;                parameter, but can also be an input parameter if /Incremental 
;                is set.  
; OPTIONAL INPUT KEYWORD:
;
;      /FROM_IEEE - If this keyword is set, then the input is assumed to be in
;           big endian format (e.g. an untranslated FITS array).   This keyword
;           only has an effect on little endian machines (e.g. Linux boxes).
;
;      /Incremental - If this keyword is set, use the checksum
;           parameter as input of a partial checksum from a previous
;           call, allowing checksums for large arrays to be calculated 
;           incrementally. 
;
;      /NoSAVE - if set, then the input array is not saved upon exiting.   Use 
;           the /NoSave keyword to save time if the input array is not needed 
;           in further computations. 
; METHOD:
;       Uses TOTAL() to sum the array into an unsigned integer variable.  The
;       overflow bits beyond 2^32 are then shifted back to the least significant
;       bits.    The summing is done in chunks of 2^31 numbers to avoid loss
;      of precision.    Adapted from FORTRAN code in
;      heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/checksum/node30.html
;
; RESTRICTIONS:
;       (1) Not valid for object or pointer data types
; EXAMPLE:
;        (1) Find the 32 bit checksum of the array x = findgen(35)
;
;       IDL> checksum32, x, s    ===> s =  2920022024
;
;        (2) Find the checksum of an array too large to fit in memory at one time
;            by breaking it up into 15 pieces.
;
;       IDL> a = randomn(seed,100,100, 15)
;		IDL> for i=0,14 do checksum32,a[*, *,i], checksum_incr, /incremental
;
;
; FUNCTION CALLED:
;       HOST_TO_IEEE, IS_IEEE_BIG(), N_BYTES()
; MODIFICATION HISTORY:
;       Written    W. Landsman          June 2001
;       Work correctly on little endian machines, added /FROM_IEEE and /NoSave
;                  W. Landsman          November 2002
;       Pad with zeros when array size not a multiple of 4 W.Landsman Aug 2003
;       Always copy to new array, somewhat slower but more robust algorithm
;           especially for Linux boxes   W. Landsman Sep. 2004 
;       Sep. 2004 update not implemented correctly (sigh) W. Landsman Dec 2004         
;       No need to byteswap 4 byte datatypes on little endian W. L. May 2009
;       Use /INTEGER keyword to TOTAL() function W.L. June 2009
;       Allow incremental calculation of checksums. Mats Löfdahl July 2019.
;       
;-
  if N_params() LT 2 then begin
     print,'Syntax - CHECKSUM32, array, checksum, /FROM_IEEE, /NoSAVE'
     return
 endif
 idltype = size(array,/type)

; Convert data to byte.  If array size is not a multiple of 4, then we pad with
; zeros 

 N = N_bytes(array)
 Nremain = N mod 4
 if Nremain GT 0 then begin 
     if keyword_set(nosave) then $
           uarray = [ byte(temporary(array),0,N), bytarr(4-Nremain)]  $
           else uarray =  [ byte(array,0,N), bytarr(4-Nremain)] 
      N = N + 4 - Nremain 
 endif else  begin 
      if keyword_set(nosave) then $
           uarray =  byte( temporary(array) ,0,N) else $
           uarray =  byte( array ,0,N) 
 endelse
 	    
; Get maximum number of base 2 digits available in an unsigned long array, 
; without losing any precision.    Since we will sum unsigned longwords, the 
; original  array must be byteswapped as longwords.

 maxnum = long64(2)^31       
 Niter =  (N-1)/maxnum
; If keyword incremental is set, use existing value of checksum as input.
 if ~keyword_set(incremental) || n_elements(checksum) eq 0 then checksum = long64(0)
 word32 =  long64(2)^32
 bswap  = ~is_ieee_big()
 if bswap then begin
       if ~keyword_set( from_ieee) then begin 
            if (idltype NE 3) && (idltype NE 4) then begin 
	         if idltype NE 1 then host_to_ieee, uarray,idltype=idltype   
                 byteorder,uarray,/NTOHL
	   endif	 
       endif else byteorder,uarray,/NTOHL	     
 endif
 
 for i=0, Niter do begin

   if i EQ Niter then begin 
           nbyte = (N mod maxnum) 
           if nbyte EQ 0 then nbyte = maxnum
   endif else nbyte = maxnum

   checksum += total(ulong(  uarray,maxnum*i,nbyte/4), /integer)
; Fold any overflow bits beyond 32 back into the word.

   hibits = long(checksum/word32)
   while hibits GT 0 do begin
     checksum = checksum - (hibits*word32) + hibits    
     hibits = long(checksum/word32)
  endwhile

   checksum = ulong(checksum)

 endfor

 checksum_part = checksum
 
 return
 end


