#!/usr/bin/python -tt
#=======================================================================
#                        General Documentation

"""Single-function module.

   See function docstring for description.
"""

#-----------------------------------------------------------------------
#                       Additional Documentation
#
# RCS Revision Code:
#   $Id: has_close.py,v 1.1 2004/02/14 01:00:27 jlin Exp $
#
# Modification History:
# - 13 Feb 2004:  Original by Johnny Lin, Computation Institute,
#   University of Chicago.  Passed passably reasonable tests.
#
# Notes:
# - Written for Python 2.2.2.
# - Function is based on code from the MA module by Paul F. Dubois.
#   Some snippets of code in this function are copied directly from 
#   lines in that module.
# - Module docstrings can be tested using the doctest module.  To
#   test, execute "python has_close.py".
# - See import statements throughout for packages/modules required.
#
# Copyright (c) 2004 by Johnny Lin.  For licensing, distribution 
# conditions, contact information, and additional documentation see
# the URL http://www.johnny-lin.com/pylib.html.
#=======================================================================




#--------------------------- General Function --------------------------

def has_close(data, value, rtol=1.e-5, atol=1.e-8):
    """Test if data has any values "equal" to value.

    Returns 1 if any of the elements of argument data has a value 
    "equal" to argument value; returns 0 otherwise.  If data or value
    is floating point, "equal" means where abs(data-value) <= atol + 
    rtol * abs(value).  This is essentially the same algorithm used 
    in the Numeric function allclose.  If data and value are integer,
    "equal" means strict equality.

    Positional Input Arguments:
    * data:   Data.  Scalar or Numeric array, Python list/tuple of
              any size and shape.
    * value:  Test value.  Scalar or 1 element Numeric array, list,
              or tuple.

    Keyword Input Arguments:
    * rtol:   "Relative" tolerance.  Default is 1.e-5.  Used in the
              comparison between data and value only if the two are 
              floating point.
    * atol:   "Absolute" tolerance.  Default is 1.e-8.  Used in the
              comparison between data and value only if the two are 
              floating point.

    Examples:
    >>> from has_close import has_close
    >>> data = [20., -32., -1., 2., 5., 29.]
    >>> has_close(data, -1.)
    1
    >>> has_close(data, 10.)
    0
    """
    import MA
    import Numeric as N
    abs = N.absolute


    #- Make sure data is Numeric type and value is a scalar within the
    #  function:

    dataN  = N.array(data)
    valueS = N.array(value)[0]


    #- Safe compare if floating.  Strict compare if integer.  Any other
    #  type returns an error:

    if (dataN.typecode() in N.typecodes['Float']) or \
       (type(valueS) == type(1.)):
        closemask = N.less_equal(abs(dataN-valueS), atol+rtol*abs(valueS))
    elif (dataN.typecode() in N.typecodes['Integer']) and \
         (type(valueS) == type(1)):
        closemask = N.where(dataN == valueS, 1, 0)
    else:
        raise ValueError, "has_close:  Inputs must be Float or Integer"


    #- Return true if any elements of data has value:

    if MA.maximum(closemask) == 1:
        return 1
    else:
        return 0




#-------------------------- Main:  Test Module -------------------------

#- Define additional examples for doctest to use:

__test__ = { 'Additional Examples':
    """
    >>> from has_close import has_close
    >>> import Numeric as N
    >>> data = N.arange(20.)
    >>> has_close(data, -1.)
    0
    >>> data = N.arange(20.)
    >>> has_close(data, 10.)
    1
    >>> data = [3, 2, 5, 10, -2, -3, 63]
    >>> has_close(data, -2)
    1
    >>> data = [3, 2, 5, 10, -2, -3, 63]
    >>> has_close(data, -22)
    0
    >>> data = [3, 2, 5, 10, -2, -3, 63]
    >>> has_close(data, -1.9999999999)
    1
    >>> data = [3, 2, 5, 10, -2, -3, 63]
    >>> has_close(data, -2.01)
    0
    >>> data = N.arange(20.)
    >>> has_close(data, 10)
    1
    >>> data = N.arange(20.)
    >>> has_close(data, [10])
    1
    >>> data = N.arange(20.)
    >>> has_close(data, N.array(15.))
    1
    >>> data = N.arange(20.)
    >>> has_close(data, N.array(50.))
    0
    >>> data = 4.
    >>> has_close(data, 2.)
    0
    >>> data = 4.
    >>> has_close(data, 4.)
    1
    >>> data = 4.
    >>> has_close(data, 3.9)
    0
    """ }


#- Execute doctest if module is run from command line:

if __name__ == "__main__":
    """Test the module.

    Tests the examples in all the module documentation 
    strings, plus __test__.

    Note:  To help ensure that module testing of this file works, the
    parent directory to the current directory is added to sys.path.
    """
    import doctest, sys, os
    sys.path.append(os.pardir)
    doctest.testmod(sys.modules[__name__])




# ===== end file =====
