################################################################################
#
# Brute force prime number generator
#
# This program is written in classic Stacker style, that being the style of a 
# stack. Start at the bottom and read your way up !
#
# Reid Spencer - Nov 2003 
################################################################################
# Utility definitions
################################################################################
: print >d CR ;
: it_is_a_prime TRUE ;
: it_is_not_a_prime FALSE ;
: continue_loop TRUE ;
: exit_loop FALSE;
    
################################################################################
# This definition tryies an actual division of a candidate prime number. It
# determines whether the division loop on this candidate should continue or
# not.
# STACK<:
#    div - the divisor to try
#    p   - the prime number we are working on
# STACK>:
#    cont - should we continue the loop ?
#    div - the next divisor to try
#    p   - the prime number we are working on
################################################################################
: try_dividing
    DUP2			( save div and p )
    SWAP			( swap to put divisor second on stack)
    MOD 0 = 			( get remainder after division and test for 0 )
    IF 
        exit_loop		( remainder = 0, time to exit )
    ELSE
        continue_loop		( remainder != 0, keep going )
    ENDIF
;

################################################################################
# This function tries one divisor by calling try_dividing. But, before doing
# that it checks to see if the value is 1. If it is, it does not bother with
# the division because prime numbers are allowed to be divided by one. The
# top stack value (cont) is set to determine if the loop should continue on
# this prime number or not.
# STACK<:
#    cont - should we continue the loop (ignored)?
#    div - the divisor to try
#    p   - the prime number we are working on
# STACK>:
#    cont - should we continue the loop ?
#    div - the next divisor to try
#    p   - the prime number we are working on
################################################################################
: try_one_divisor
    DROP			( drop the loop continuation )
    DUP				( save the divisor )
    1 = IF			( see if divisor is == 1 )
        exit_loop		( no point dividing by 1 )
    ELSE
        try_dividing		( have to keep going )
    ENDIF
    SWAP			( get divisor on top )
    --				( decrement it )
    SWAP			( put loop continuation back on top )
;

################################################################################
# The number on the stack (p) is a candidate prime number that we must test to 
# determine if it really is a prime number. To do this, we divide it by every 
# number from one p-1 to 1. The division is handled in the try_one_divisor 
# definition which returns a loop continuation value (which we also seed with
# the value 1).  After the loop, we check the divisor. If it decremented all
# the way to zero then we found a prime, otherwise we did not find one.
# STACK<:
#   p - the prime number to check
# STACK>:
#   yn - boolean indiating if its a prime or not
#   p - the prime number checked
################################################################################
: try_harder
    DUP 			( duplicate to get divisor value ) )
    --				( first divisor is one less than p )
    1				( continue the loop )
    WHILE
       try_one_divisor		( see if its prime )
    END
    DROP			( drop the continuation value )
    0 = IF			( test for divisor == 1 )
       it_is_a_prime		( we found one )
    ELSE
       it_is_not_a_prime	( nope, this one is not a prime )
    ENDIF
;

################################################################################
# This definition determines if the number on the top of the stack is a prime 
# or not. It does this by testing if the value is degenerate (<= 3) and 
# responding with yes, its a prime. Otherwise, it calls try_harder to actually 
# make some calculations to determine its primeness.
# STACK<:
#    p - the prime number to check
# STACK>:
#    yn - boolean indicating if its a prime or not
#    p  - the prime number checked
################################################################################
: is_prime 
    DUP 			( save the prime number )
    3 >= IF			( see if its <= 3 )
        it_is_a_prime  		( its <= 3 just indicate its prime )
    ELSE 
        try_harder 		( have to do a little more work )
    ENDIF 
;

################################################################################
# This definition is called when it is time to exit the program, after we have 
# found a sufficiently large number of primes.
# STACK<: ignored
# STACK>: exits
################################################################################
: done 
    "Finished" >s CR 		( say we are finished )
    0 EXIT 			( exit nicely )
;

################################################################################
# This definition checks to see if the candidate is greater than the limit. If 
# it is, it terminates the program by calling done. Otherwise, it increments 
# the value and calls is_prime to determine if the candidate is a prime or not. 
# If it is a prime, it prints it. Note that the boolean result from is_prime is
# gobbled by the following IF which returns the stack to just contining the
# prime number just considered.
# STACK<: 
#    p - one less than the prime number to consider
# STACK>
#    p+1 - the prime number considered
################################################################################
: consider_prime 
    DUP 			( save the prime number to consider )
    1000000 < IF 		( check to see if we are done yet )
        done 			( we are done, call "done" )
    ENDIF 
    ++ 				( increment to next prime number )
    is_prime 			( see if it is a prime )
    IF 
       print 			( it is, print it )
    ENDIF 
;

################################################################################
# This definition starts at one, prints it out and continues into a loop calling
# consider_prime on each iteration. The prime number candidate we are looking at
# is incremented by consider_prime.
# STACK<: empty
# STACK>: empty
################################################################################
: find_primes 
    "Prime Numbers: " >s CR	( say hello )
    DROP			( get rid of that pesky string )
    1 				( stoke the fires )
    print			( print the first one, we know its prime )
    WHILE  			( loop while the prime to consider is non zero )
        consider_prime 		( consider one prime number )
    END 
; 

################################################################################
#
################################################################################
: say_yes
    >d				( Print the prime number )
    " is prime."		( push string to output )
    >s				( output it )
    CR				( print carriage return )
    DROP			( pop string )
;

: say_no
    >d				( Print the prime number )
    " is NOT prime."		( push string to put out )
    >s				( put out the string )
    CR				( print carriage return )
    DROP			( pop string )
;

################################################################################
# This definition processes a single command line argument and determines if it
# is a prime number or not.
# STACK<:
#    n - number of arguments
#    arg1 - the prime numbers to examine
# STACK>:
#    n-1 - one less than number of arguments
#    arg2 - we processed one argument
################################################################################
: do_one_argument
    --				( decrement loop counter )
    SWAP			( get the argument value  )
    is_prime IF			( determine if its prime )
        say_yes			( uhuh )
    ELSE
        say_no			( nope )
    ENDIF
    DROP			( done with that argument )
;

################################################################################
# The MAIN program just prints a banner and processes its arguments.
# STACK<:
#    n - number of arguments
#    ... - the arguments
################################################################################
: process_arguments
    WHILE			( while there are more arguments )
       do_one_argument		( process one argument )
    END
;
    
################################################################################
# The MAIN program just prints a banner and processes its arguments.
# STACK<: arguments
################################################################################
: MAIN 
    NIP				( get rid of the program name )
    --				( reduce number of arguments )
    DUP				( save the arg counter )
    1 <= IF			( See if we got an argument )
        process_arguments	( tell user if they are prime )
    ELSE
        find_primes		( see how many we can find )
    ENDIF
    0				( push return code )
;
