Sum

March 25, 2011

Here is our version of the program:

#! /usr/bin/scheme --script

(define (sum)
  (let loop ((c (read-char)) (s 0) (b 0))
    (if (eof-object? c)
        (values s (+ (quotient b 512) (if (zero? (modulo b 512)) 0 1)))
        (loop (read-char) (modulo (+ s (char->integer c)) 65535) (+ b 1)))))

(if (null? (cdr (command-line)))
    (let-values (((s b) (sum)))
      (display s) (display " ") (display b) (newline))
    (do ((files (cdr (command-line)) (cdr files))) ((null? files))
      (with-input-from-file (car files)
        (lambda ()
          (let-values (((s b)(sum)))
            (display s) (display " ")
            (display b) (display " ")
            (display (car files)) (newline))))))

The first line and the (command-line) function are unique to Chez Scheme; (command-line) returns the command line by which the program was called, including the name of the program in its car. The main body of the program is a two-legged if; the first leg handles input from stdin and the second leg handles input from named files. Scheme provides no standard way to trap file-opening errors, so we don’t implement the return codes of the original sum command; we could do so if we used a Chez-specific error handler, but the details aren’t germane here, so we simply ignore the issue.

The (sum) function calculates the checksum and block size for the current input port, which is set in the main body of the code. The checksum adds the values of the bytes in the file, taking each partial sum modulo 216−1. The calculation of the block size is careful to add 1 for a partial block at the end of the file.

The code is assembled at http://programmingpraxis.codepad.org/ml5SKPg0.

Pages: 1 2

10 Responses to “Sum”

  1. Martin Oldfield said

    I’m not sure that this is quite in the spirit of the site but:

    $ /usr/local/bin/sum -s /usr/share/dict/words 
    19278 4858 /usr/share/dict/words
    $ perl -le 'local $/; print unpack("%32W*", <>) % 65535' /usr/share/dict/words 
    19278
    
    
  2. My Haskell solution (see http://bonsaicode.wordpress.com/2011/03/25/programming-praxis-sum/ for a version with comments):

    import Data.Char
    import System.Environment
    
    checksum :: String -> String
    checksum = (\(s,b) -> show s ++ " " ++ show (div (b + 511) 512)) .
        foldl (\(s,b) c -> (mod (s + ord c) 65535, b + 1)) (0,0)
    
    main :: IO ()
    main = getArgs >>= \args -> case args of
        [] -> interact checksum
        fs -> mapM_ (\f -> putStrLn . (++ ' ':f) . checksum =<< readFile f) fs
    
  3. […] today’s Programming Praxis exercise, our goal is to implement a unix checksum utility. Let’s get […]

  4. Graham said
    #!/usr/bin/env python
    
    import sys
    
    
    def checksum(f):
        s = b = 0
        for c in f.read():
            s = (s + ord(c)) % 65535
            b += 1
        p = 0 if (b % 512 == 0) else 1
        return s, (b // 512) + p
    
    
    def main(args=None):
        if args:
            for arg in args:
                with open(arg) as f:
                    s, b = checksum(f)
                    print "{0}\t{1}\t{2}".format(s, b, arg)
        else:
            s, b = checksum(sys.stdin)
            print "{0}\t{1}".format(s, b)
        return None
    
    
    if __name__ == "__main__":
        main(sys.argv[1:])
    
  5. John said

    I implemented a version in Factor (the short version is below):

    : sum-file ( path -- )
        [
            binary file-contents sum
            [ 65535 mod ] [ 512 / ceiling ] bi
        ] [ "%d %d %s\n" printf ] bi ;
    

    The full version is here:

    http://re-factor.blogspot.com/2011/03/sum.html

  6. Mike said
    import fileinput
    
    def report(checksum, bytecount, filename=''):
        return '{:5} {:5} {}'.format(checksum&0xffff, (bytecount+511)/512, filename)
    
    filename = None
    
    for line in fileinput.input():
        if fileinput.isfirstline():
            if filename is not None:
                print report(checksum, bytecount, filename)
    
            filename = fileinput.filename()
                
            bytecount  = 0
            checksum = 0
    
        bytecount += len(line)
        checksum += sum(ord(c) for c in line)
    
    print report(checksum, bytecount, filename)
    
  7. John said

    Whoops, mis-understood the requirements – this is a fixed version for Factor:

    : sum-file. ( path -- )
        [
            binary file-contents
            [ sum 65535 mod ] [ length 512 / ceiling ] bi
        ] [ "%d %d %s\n" printf ] bi ;
    
  8. Ross said

    Here’s a go in Common Lisp using LOOP, also had a bit of fun on tweaking the reading of the file and inlining the calculation:


    (defun sv4r-sum (file)
    "Calculate a Unix SV4R-style file checksum"
    (with-open-file (stream file :direction :input :element-type 'unsigned-byte)
    (loop :with buffer = (make-array 512 :element-type 'unsigned-byte)
    :and csum = 0
    :for pos = (read-sequence buffer stream)
    :while (plusp pos)
    :do (loop :for b :from 0 :to (1- pos)
    :do (setf csum (mod (+ csum (svref buffer b)) 65535)))
    :counting buffer :into c
    :finally (return (values csum c)))))

  9. Ian Price said

    You can never have to many Scheme solutions ;)

    #!r6rs
    (import (rnrs)
            (wak foof-loop) ;; I ♥ foof loop
            (srfi :48 intermediate-format-strings))
    
    (define (print-sum port name)
      (let-values (((checksum num-blocks) (sum port)))
        (format #t "~a ~a ~a~%" checksum num-blocks name)))
    
    (define (sum port)
      (loop ((for byte (in-port port get-u8))
             (for num-bytes (up-from 0))
             (for total (summing byte)))
            => (values (mod total (- (expt 2 16) 1))
                       (ceiling (/ num-bytes 512)))))
    
    (let ((args (cdr (command-line))))
      (if (null? args)
          (print-sum (standard-input-port) "")
          (for-each (lambda (file)
                      (call-with-port (open-file-input-port file)
                        (lambda (out)
                          (print-sum out file))))
                    args)))
    
  10. Jebb said
    #include <stdio.h>
    
    void check_sum(FILE *fp);
    
    int main(int argc, char *argv[])
    {
        FILE *fp;
        if (argc == 1)
            check_sum(stdin);
        else 
            while (argc-- > 1)
                if ((fp = fopen(*++argv, "r"))== NULL) {
                    fprintf(stderr, "couldn't open file %s\n", *argv);
                    return 1;
                }   
                else {
                    check_sum(fp);
                    fclose(fp);
                }   
        return 0;
    }
    
    void check_sum(FILE *fp)
    {
        unsigned int bytes_sum; //Guaranteed to go at least to 65535
        unsigned int bytes_count;
        int next_byte;
        bytes_count = bytes_sum = 0;
        while ((next_byte = getc(fp)) != EOF) {
            bytes_sum = (bytes_sum + next_byte) % 65535;
            bytes_count++;
        }   
        printf("%d %d\n", bytes_sum, 1 + bytes_count / 512); 
    }

Leave a comment