#!/usr/bin/python
# fn_math.py $Revision: 1.8 $ $Date: 2006/02/05 01:03:45 $
# This library contains built-in math related functions.

import objectifier
build_GG_command= objectifier.build_GG_command

# =============== Built In Functions ===========================
internal_syms= dict() # Establish the list of built-in functions.

# CONSTANTS
# ---------
internal_syms['e_']= objectifier.StackOB_VAL( 2.7182818284590451 )
internal_syms['pi']= objectifier.StackOB_VAL( 3.1415926535897931 )

# FUNCTIONS
# ---------
def intf_MUL(E):
    """Simple multiplication."""
    term1= E.The.StackPop()
    term2= E.The.StackPop()
    E.The.StackPush(objectifier.StackOB_VAL(term1.val * term2.val))
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), term2, term1 ] )
internal_syms['mul']= intf_MUL # Register function.
internal_syms['*']= intf_MUL # Register function.
internal_syms['neg']= build_GG_command('-1 mul')
    
def intf_ADD(E):
    """Simple addition."""
    term1= E.The.StackPop()
    term2= E.The.StackPop()
    if term1.whatami == 'TXT':
        E.The.StackPush(objectifier.StackOB_TXT(term2.val + term1.val))
    elif term1.whatami == 'VAL':
        E.The.StackPush(objectifier.StackOB_VAL(term1.val + term2.val))
    elif term1.whatami == 'LST':
        E.The.StackPush(objectifier.StackOB_LST(term1.val + term2.val))
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), term2, term1 ] )
internal_syms['add']= intf_ADD # Register function.
internal_syms['+']= intf_ADD # Register function.
internal_syms['sub']= build_GG_command('neg add')
internal_syms['-']= internal_syms['sub']

def intf_INCR(E):
    """An increment function. Will increment a value by adding 1. Will
    also try to add one to the value of a symbol (leaving nothing).
    Incrementing text will append an extra copy of the last letter of
    the original string. Lists increment by adding another copy of the
    last item."""
    term= E.The.StackPop()
    if term.whatami == 'SYM':
        if E.symtab[term.val].whatami == 'VAL':
            # Needs a new object to break old refs links.
            v= objectifier.StackOB_VAL( E.symtab[term.val].val + 1 )
            E.symtab[term.val]= v
        elif E.symtab[term.val].whatami == 'TXT':
            # Needs a new object to break old refs links.
            t= objectifier.StackOB_TXT( E.symtab[term.val].val +
                                        E.symtab[term.val].val[-1] )
            E.symtab[term.val]= t
        elif E.symtab[term.val].whatami == 'LST':
            # Needs a new object to break old refs links.
            t= objectifier.StackOB_LST( E.symtab[term.val].val +
                                       [E.symtab[term.val].val[-1]] )
            E.symtab[term.val]= t
        elif E.symtab[term.val].whatami == 'SYM':
            pass # This is not complete. Because it's hard.
    elif term.whatami == 'VAL':
        E.The.StackPush(objectifier.StackOB_VAL(term.val + 1))
    elif term.whatami == 'TXT':
        newseq= term.val+term.val[-1]
        E.The.StackPush(objectifier.StackOB_TXT(newseq))
    elif term.whatami == 'LST':
        newseq= term.val + [ term.val[-1] ] # Orig. list + last item.
        E.The.StackPush(objectifier.StackOB_LST(newseq))
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), term ] )
internal_syms['++']= intf_INCR # Register function.

def intf_DECR(E):
    """A decrement function. Will decrease a value by subracting 1. Will
    also try to subtract one from the value of a symbol (leaving nothing).
    Decrementing text will remove the last letter of the original
    string. Lists decrement by removing the last item."""
    term= E.The.StackPop()
    if term.whatami == 'SYM':
        if E.symtab[term.val].whatami == 'VAL':
            # Needs a new object to break old refs links.
            v= objectifier.StackOB_VAL( E.symtab[term.val].val - 1 )
            E.symtab[term.val]= v
        elif E.symtab[term.val].whatami == 'TXT':
            # Needs a new object to break old refs links.
            t= objectifier.StackOB_TXT( E.symtab[term.val].val[0:-1] )
            E.symtab[term.val]= t
        elif E.symtab[term.val].whatami == 'LST':
            # Needs a new object to break old refs links.
            t= objectifier.StackOB_LST( E.symtab[term.val].val[0:-1] )
            E.symtab[term.val]= t
        elif E.symtab[term.val].whatami == 'SYM':
            pass # This is not complete. Because it's hard.
    elif term.whatami == 'VAL':
        E.The.StackPush(objectifier.StackOB_VAL(term.val - 1))
    elif term.whatami == 'TXT':
        newseq= term.val[0:-1]
        E.The.StackPush(objectifier.StackOB_TXT(newseq))
    elif term.whatami == 'LST':
        newseq= term.val[0:-1] 
        E.The.StackPush(objectifier.StackOB_LST(newseq))
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), term ] )
internal_syms['--']= intf_DECR # Register function.

def intf_DIV(E):
    """Floating point division. Watch that div by zero!"""
    orig_den=  E.The.StackPop() # Preserve in case of badness.
    orig_num=  E.The.StackPop() # Preserve in case of badness.
    try:
        E.The.StackPush(
             objectifier.StackOB_VAL(orig_num.val / orig_den.val) )
        E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), orig_num, orig_den] )
    except ZeroDivisionError:
        print 'n/0 - Oldest trick in the book!'
        E.The.StackPush( orig_num ) # Run away!
        E.The.StackPush( orig_den ) # Run away!
internal_syms['div']= intf_DIV # Register function.
internal_syms['/']= intf_DIV # Register function.

# This doesn't quite work. It's close though. It doesn't preserve the
# stack through div/0.
internal_syms['inv']= build_GG_command('1 swap div')

def intf_IDIV(E):
    """Integer division... This is easy thanks to Python's stupid
    default division behavior."""
    orig_den=  E.The.StackPop() # Preserve in case of badness.
    orig_num=  E.The.StackPop() # Preserve in case of badness.
    try:
        E.The.StackPush(
            objectifier.StackOB_VAL(
                int(orig_num.val) / int(orig_den.val)) )
        E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), orig_num, orig_den] )
    except ZeroDivisionError:
        print 'n/0 - Oldest trick in the book!'
        E.The.StackPush( orig_num ) # Run away!
        E.The.StackPush( orig_den ) # Run away!
internal_syms['idiv']= intf_IDIV # Register function.

#MOD
def intf_MOD(E):
    """Software engineer's special - modolo, i.e. remainder."""
    orig_den=  E.The.StackPop() # Preserve in case of badness.
    orig_num=  E.The.StackPop() # Preserve in case of badness.
    try:
        E.The.StackPush(
            objectifier.StackOB_VAL(
                orig_num.val % orig_den.val) )
        E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), orig_num, orig_den] )
    except ZeroDivisionError:
        print 'n/0 - Oldest trick in the book!'
        E.The.StackPush( orig_num ) # Run away!
        E.The.StackPush( orig_den ) # Run away!
internal_syms['mod']= intf_MOD # Register function.

# modf(r,f)

def intf_SUM(E):
    """Calculate the sum of a list of values."""
    L=  E.The.StackPop() # Preserve in case of badness.
    if 'LST' == L.whatami:
        Lv= L.val # Now it's a list, not an object.
        sum= 0
        count= 0
        for i in Lv:
            if 'VAL' == i.whatami:
                sum += i.val
                count += 1
        Answer= objectifier.StackOB_VAL( sum )
    E.The.StackPush( Answer ) # Put something back.
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), L ] )
internal_syms['sum']= intf_SUM # Register function.

def intf_AVG(E):
    """Calculate the average of a list of values."""
    L=  E.The.StackPop() # Preserve in case of badness.
    if 'LST' == L.whatami:
        Lv= L.val # Now it's a list, not an object.
        sum= 0
        count= 0
        for i in Lv:
            if 'VAL' == i.whatami:
                sum += i.val
                count += 1
        if count:
            Answer= objectifier.StackOB_VAL( sum / count )
        else:
            Answer= objectifier.StackOB_VAL( 0 )
    E.The.StackPush( Answer ) # Put something back.
    E.Undo.StackPush( [ objectifier.StackOB_SYM('drop'), L ] )
internal_syms['avg']= intf_AVG # Register function.

# VECTORS
# -------
#CROSS
#DOT
#VADD
#VSUB

