my %algorithm = (
    sum => sub {
        my $bits = 0;
        $bits += ord foreach (split //);
        return $bits;
    },

    sum_length_seed => sub {
        my $bits = length;
        $bits += ord foreach (split //);
        return $bits;
    },

    eor => sub {
        my $bits = 0;
        $bits ^= ord foreach (split //);
        return $bits;
    },

    eor_then_swap => sub {
        my $bits = 0;
        foreach (split //) {
            $bits ^= ord;
            $bits = (($bits >> 4) | ($bits << 4)) & 0xff;
        }
        return $bits;
    },

    shift_then_eor => sub {
        my $bits = 0;
        foreach (split //) {
            $bits <<= 1;
            $bits ^= ord;
        }
        return $bits;
    },

    eor_then_roll => sub {
        my $bits = 0;
        my $carry = 0;
        foreach (split //) {
            $bits ^= ord;
            $bits = ($bits << 1) | $carry;
            $carry = ($bits & 256) ? 1 : 0;
            $bits &= 255;
        }
        return $bits;
    },

    lua => sub {
        my $bits = length;
        $bits ^= (($bits << 5) + ($bits >> 2) + ord) foreach (split //);
        return $bits;
    },
);


my %divisor = (
    mod_7  => sub { return abs($_[0] %  7); },
    mod_11 => sub { return abs($_[0] % 11); },
    mod_13 => sub { return abs($_[0] % 13); },
    mod_16 => sub { return abs($_[0] % 16); },
    mod_17 => sub { return abs($_[0] % 17); },

    add_nybs => sub {
        my $hash = shift;
        $hash += ($hash >> 4);
        return $hash & 0xf;
    },
);


my %count;
my $words = 0;

while (<DATA>) {
    foreach (split) {
        foreach $alg (keys %algorithm) {
            foreach $div (keys %divisor) {
                my $hash = $divisor{$div}($algorithm{$alg}($_));
                $count{$alg}{$div}{$hash}++;
                $count{$alg}{$div}{"n"} = $hash if ($hash > $count{$alg}{$div}{"n"});
            }
        }
        $words++;
    }
}

foreach my $div (sort keys %divisor) {
    print "$div\n";
    foreach my $alg (sort keys %algorithm) {
        printf("    %-15s: ", $alg);

        my $highest = $count{$alg}{$div}{"n"};
        my $ideal = $words/($highest+1);

        my $below = 0;
        my $above = 0;
        my $threshold = 3;

        foreach (0 .. $highest) {
            $delta = $count{$alg}{$div}{$_} - $ideal;
            $below -= $delta if ($delta < -$threshold);
            $above += $delta if ($delta >  $threshold);
            printf("%3d ", $delta);
        }
        printf("--> score=%3d\n", $below+$above);
    }
    print "\n";
}

__DATA__
< <> <# = > - , ; : ! ? / . ." .( ' ( [ ['] ] @ * */ \ # #> + +!
0< 0<> 0= 0> 1- 1+ 2! 2/ 2@ 2* 2CONSTANT 2DROP 2DUP 2LITERAL 2OVER 2>R
2R> 2R@ 2ROT 2SWAP 2VARIABLE ABORT ABORT" ABS ACCEPT AGAIN AHEAD ALIGN
ALIGNED ALLOCATE ALLOT ALSO AND ASSEMBLER AT-XY BASE BEGIN BIN BL BLANK
BLK BLOCK >BODY BUFFER BYE C, C! C" C@ CASE CATCH CELL+ CELLS CHAR [CHAR]
CHAR+ CHARS CLOSE-FILE CMOVE CMOVE> CODE ;CODE COMPARE [COMPILE] COMPILE,
CONSTANT CONVERT COUNT CR CREATE CREATE-FILE CS-PICK CS-ROLL D< D= D- D.
D+ D0< D0= D2/ D2* DABS DECIMAL DEFINITIONS DELETE-FILE DEPTH D>F DF!
DF@ DFALIGN DFALIGNED DFLOAT+ DFLOATS DMAX DMIN DNEGATE DO ?DO DOES>
D.R DROP D>S DU< DUMP DUP ?DUP EDITOR EKEY EKEY? EKEY>CHAR ELSE [ELSE]
EMIT EMIT? EMPTY-BUFFERS ENDCASE ENDOF ENVIRONMENT? ERASE EVALUATE
EXECUTE EXIT EXPECT F~ F< F- F! F/ F. F@ F* F** F+ F0< F0= FABS
FACOS FACOSH FALIGN FALIGNED FALOG FALSE FASIN FASINH FATAN FATAN2
FATANH FCONSTANT FCOS FCOSH F>D FDEPTH FDROP FDUP FE. FEXP FEXPM1
FILE-POSITION FILE-SIZE FILE-STATUS FILL FIND FLITERAL FLN FLNP1 >FLOAT
FLOAT+ FLOATS FLOG FLOOR FLUSH FLUSH-FILE FMAX FMIN FM/MOD FNEGATE FORGET
FORTH FORTH-WORDLIST FOVER FREE FROT FROUND FS. FSIN FSINCOS FSINH FSQRT
FSWAP FTAN FTANH FVARIABLE GET-CURRENT GET-ORDER HERE HEX HOLD I IF [IF]
IMMEDIATE >IN INCLUDED INCLUDE-FILE INVERT J KEY KEY? LEAVE LIST LITERAL
LOAD (LOCAL) LOCALS| LOOP +LOOP LSHIFT M* M*/ M+ MARKER MAX MIN MOD /MOD
*/MOD MOVE MS NEGATE NIP :NONAME >NUMBER OF ONLY OPEN-FILE OR ORDER OVER
PAD PAGE PARSE PICK POSTPONE PRECISION PREVIOUS QUERY QUIT >R .R R> R@
READ-FILE READ-LINE RECURSE REFILL RENAME-FILE REPEAT REPOSITION-FILE
REPRESENT RESIZE RESIZE-FILE RESTORE-INPUT R/O ROLL ROT RSHIFT R/W .S #S
S" SAVE-BUFFERS SAVE-INPUT SCR S>D SEARCH SEARCH-WORDLIST SEE SET-CURRENT
SET-ORDER SET-PRECISION SF! SF@ SFALIGN SFALIGNED SFLOAT+ SFLOATS SIGN
SLITERAL SM/REM SOURCE SOURCE-ID SPACE SPACES SPAN STATE /STRING SWAP THEN
[THEN] THROW THRU TIB #TIB TIME&DATE TO -TRAILING TRUE TUCK TYPE U< U> U.
UM* UM/MOD UNLOOP UNTIL UNUSED UPDATE U.R VALUE VARIABLE WHILE WITHIN
W/O WORD WORDLIST WORDS WRITE-FILE WRITE-LINE XOR
