""" This module provides access to higher level functions and
constants for ieee special values such as Not a Number (nan) and
infinity (inf).

>>> from numarray import *

>>> inf    # the repr() of inf may vary from platform to platform
inf
>>> nan    # the repr() of nan may vary from platform to platform
nan

# Create a couple inf values in 4,4 array

>>> a=arange(16.0, shape=(4,4))
>>> a[2,3] = 0.0
>>> b = 1/a
Warning: Encountered divide by zero(s)  in divide

# Locate the positions of the inf values

>>> getinf(b)
(array([0, 2]), array([0, 3]))

# Change the inf values to something else

>>> a[getinf(b)] = 999
>>> a
array([[ 999.,    1.,    2.,    3.],
       [   4.,    5.,    6.,    7.],
       [   8.,    9.,   10.,  999.],
       [  12.,   13.,   14.,   15.]])

# Set a bunch of locations to a special value

>>> a[0,1] = nan; a[1,2] = nan; a[2,3] = nan
>>> getnan(a)
(array([0, 1, 2]), array([1, 2, 3]))
"""

import numarrayall as _na
import ieeespecial as _spec

# Define *ieee special values*
_na.Error.pushMode(all="ignore")
plus_inf = inf = _na.array(1.0)/_na.array(0.0)
minus_inf = _na.array(-1.0)/_na.array(0.0)    
nan = _na.array(0.0)/_na.array(0.0)
_na.Error.popMode()

# Define *mask condition bits*
def BIT(x):
    return _na.array([(1 << x)], type=_na.Int32)

POS_QUIET_NAN     = BIT(0)
NEG_QUIET_NAN     = BIT(1)
POS_SIGNAL_NAN    = BIT(2)
NEG_SIGNAL_NAN    = BIT(3)
POS_INFINITY      = BIT(4)
NEG_INFINITY      = BIT(5)
POS_DENORMALIZED  = BIT(6)
NEG_DENORMALIZED  = BIT(7)
POS_NORMALIZED    = BIT(8)
NEG_NORMALIZED    = BIT(9)
POS_ZERO          = BIT(10)
NEG_ZERO          = BIT(11)
INDETERM          = BIT(12)
BUG               = BIT(15)

NAN = POS_QUIET_NAN | NEG_QUIET_NAN | POS_SIGNAL_NAN | NEG_SIGNAL_NAN | INDETERM
INFINITY = POS_INFINITY | NEG_INFINITY
SPECIAL = NAN | INFINITY

NORMALIZED = POS_NORMALIZED | NEG_NORMALIZED
DENORMALIZED = POS_DENORMALIZED | NEG_DENORMALIZED
ZERO = POS_ZERO | NEG_ZERO
NUMBER = NORMALIZED | DENORMALIZED | ZERO

def mask(a, m):
    """mask(a, m) returns the values of 'a' satisfying category 'm'.
    mask does a parallel check for values which are not classifyable
    by the categorization code, raising a RuntimeError exception if
    any are found.
    """
    a = _na.asarray(a)
    if isinstance(a.type(), _na.IntegralType):
        raise TypeError("Integer arrays can't represent IEEE special values.")
    f = _na.ieeemask(a, m)
    g = _na.ravel(_na.ieeemask(a, BUG))
    if _na.bitwise_or.reduce(g) != 0:
        raise RuntimeError("Unclassifyable floating point values.")
    return f

def index(a, msk):
    """index returns the tuple of indices where the values satisfy 'mask'"""
    return _na.nonzero(mask(a, msk))

def getinf(a):
    """getinf returns a tuple of indices of 'a' where the values are infinite."""
    return _spec.index(a, _spec.INFINITY)

def setinf(a, value):
    """setinf sets elements of 'a' which are infinite to 'value' instead.
    DEPRECATED:  use 'a[getinf(a)] = value'  instead.
    """
    _na.put(a, getinf(a), value)

def getposinf(a):
    """getposinf returns a tuple of indices of 'a' where the values are +inf."""
    return _spec.index(a, _spec.POS_INFINITY)

def getneginf(a):
    """getneginf returns a tuple of indices of 'a' where the values are -inf."""
    return _spec.index(a, _spec.NEG_INFINITY)

def getnan(a):
    """getnan returns a tuple of indices of 'a' where the values are not-a-numbers"""
    return _spec.index(a, _spec.NAN)

def setnan(a, value):
    """setnan sets elements of 'a' which are NANs to 'value' instead.
    DEPRECATED:  use 'a[getnan(a)] = value'  instead.
    """
    _na.put(a, getnan(a), value)

def getbug(a):
    """getbug returns a tuple of indices of 'a' where the values are not classifyable."""
    return _spec.index(a, _spec.BUG)

def test():
    import doctest, ieeespecial
    return doctest.testmod(ieeespecial)

