[Scipy-svn] r3817 - in trunk/scipy/sandbox: . maskedarray maskedarray/alternative_versions maskedarray/tests

scipy-svn at scipy.org scipy-svn at scipy.org
Sat Jan 12 01:54:03 EST 2008


Author: matthew.brett at gmail.com
Date: 2008-01-12 00:53:41 -0600 (Sat, 12 Jan 2008)
New Revision: 3817

Added:
   trunk/scipy/sandbox/maskedarray/
   trunk/scipy/sandbox/maskedarray/.project
   trunk/scipy/sandbox/maskedarray/CHANGELOG
   trunk/scipy/sandbox/maskedarray/LICENSE
   trunk/scipy/sandbox/maskedarray/README
   trunk/scipy/sandbox/maskedarray/__init__.py
   trunk/scipy/sandbox/maskedarray/alternative_versions/
   trunk/scipy/sandbox/maskedarray/alternative_versions/core_alt.py
   trunk/scipy/sandbox/maskedarray/alternative_versions/core_ini.py
   trunk/scipy/sandbox/maskedarray/alternative_versions/core_initial.py
   trunk/scipy/sandbox/maskedarray/alternative_versions/test_mrecarray.py
   trunk/scipy/sandbox/maskedarray/bench.py
   trunk/scipy/sandbox/maskedarray/core.py
   trunk/scipy/sandbox/maskedarray/extras.py
   trunk/scipy/sandbox/maskedarray/morestats.py
   trunk/scipy/sandbox/maskedarray/mpl_maskedarray.patch
   trunk/scipy/sandbox/maskedarray/mrecords.py
   trunk/scipy/sandbox/maskedarray/mstats.py
   trunk/scipy/sandbox/maskedarray/setup.py
   trunk/scipy/sandbox/maskedarray/tests/
   trunk/scipy/sandbox/maskedarray/tests/test_core.py
   trunk/scipy/sandbox/maskedarray/tests/test_extras.py
   trunk/scipy/sandbox/maskedarray/tests/test_morestats.py
   trunk/scipy/sandbox/maskedarray/tests/test_mrecords.py
   trunk/scipy/sandbox/maskedarray/tests/test_mstats.py
   trunk/scipy/sandbox/maskedarray/tests/test_subclassing.py
   trunk/scipy/sandbox/maskedarray/testutils.py
   trunk/scipy/sandbox/maskedarray/timer_comparison.py
   trunk/scipy/sandbox/maskedarray/version.py
Log:
Added back interim copy of maskedarray - will soon be removed in favor of numpy merged version

Added: trunk/scipy/sandbox/maskedarray/.project
===================================================================
--- trunk/scipy/sandbox/maskedarray/.project	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/.project	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>scipy_svn_maskedarray</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>

Added: trunk/scipy/sandbox/maskedarray/CHANGELOG
===================================================================
--- trunk/scipy/sandbox/maskedarray/CHANGELOG	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/CHANGELOG	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,117 @@
+    The maskedarray package went through some major updates recently. 
+Originally, a MaskedArray object had two major attributes: (i) _data, a
+(subclass of) ndarray that stored the values, and (ii) _mask, a ndarray of
+booleans storing whether the values were masekd or not. This structure was
+directly derived from the original implementation of MaskedArray, available in
+numpy.core.ma.
+
+However, this approach wasn't very natural. For example, in order to access the
+data of a MaskedArray instance, one had to query the _data attribute instead of
+the instance itself. In addition, the previous implementation of MaskedArray as
+a subclass of ndarray presented some problems. Thus, most of the attributes of
+a MaskedArray object were defined as class defaults, which turned out to be
+thread-unsafe. Moreover, subclassing MaskedArray wasn't straightforward, when
+the subclass introduced new parameters in its __new__ method.
+
+The current implementation tries to alleviate these problems. The most
+significant difference is that the _data attribute is now a view of the array
+as a (subclass of) ndarray. The actual type is set by the _baseclass attribute,
+defined at the creation of a new masked array as the class of the underlying
+data. Thus, it is still possible to use a matrix object as the underlying data:
+the new masked array will then use the matrix  methods. Note that if the
+underlying data has its own attributes, these latter are not propagated to the
+MaskedArray instance, but are reinitialized at each access. In other terms, you
+will lose specific values of these attributes during processing. You should
+then consider defining a subclass of MaskedArray. Note also that because the
+_data attribute is a view, any attempt to directly set it will likely fail. For
+example, x._data = 5 will raise an AttributeError exception. You should then
+use x._data[:] = 5 instead).
+
+The _mask attibute is left unchanged. Because it is a basic attribute, it can
+be overwritten far too easily. If you want to specify a new value for the mask,
+you should use the _setmask or __setmask__ methods: these methods ensure that
+the new mask has the same shape as the data, and that the _hardmask condition
+is respected. Note that in some particular cases involving subclasses of 
+MaskedArray, the mask is not always propagated properly. It is recommended to
+set the mask of the base object, instead of trying to set the mask through a 
+view of MaskedArray.
+
+Following the suggestions of Reggie Dugard, the class defaults have been
+suppressed. Unfortunately, that required to add some extra definitions in the
+__array_finalize__ method, which tends to have a slight negative impact on
+performances. Moreover, most methods now return a view of the masked array
+instead of creating a new masked array from scratch.
+
+
+The previous implementation of MaskedArray is called core_ini.py and it can be
+found in the alternative_versions directory. This folder contains also yet
+another implementation core_alt. This latter is left for documentation purposes,
+and should serve as a template when we'll port the package to C. It introduces
+yet another attribute, _masklayer. This attribute is always a ndarray of booleans
+with the same shape as the data, that stores the values of the masked. The _mask
+attribute is then a property, that returns _masklayer or nomask depending on 
+the value of the _smallmask flag and the values of _masklayer. This approach 
+seems to solve the anomaly in mask propagation mentioned earlier. However, some
+performance tests show that this approach is significantly slower (from 10% to 
+50%) than the current implementation. It was therfore decided to leave it out of
+the main package.
+
+#...............................................................................
+2007-01-22 : core    : fixed a call to numpy.float128 on 32b machines
+2007-01-21 : core    : fixed max/min_fill_values
+           :         : fixed sort (as method and function)
+2007-01-18 : core    : fixed default filling values for unsigned int_.
+2007-01-16 : extras  : new function : `mediff1d`
+2007-01-15 : mrecords: new method: `addfield`
+           : core    : Force the mask of a masked array `x` to be copied w/...
+                     ...masked_array(x,copy=True,mask=nomask,keep_mask=true)
+2007-01-14 : mrecords: Slices are now properly supported
+           : mrecords: `filled` is now properly supported
+2007-01-12 : mrecords: Complete reorganization...
+2007-01-10 : mrecords: Defines a class of records that support masked arrays
+2007-01-08 : Core:
+           : core    : Force a reset of the class defaults after initialization
+           : core    : Modified __array_finallize__ to allow objects w/ _data...
+                     ... _mask fields to be recognized as MA
+2007-01-04 : core    : Fixed a but in masked_all_like
+2007-01-02 : extras  : Force apply_along_axis to output the proper fill_value 
+           : core    : Can use dtypes for the definition of default_fill_value
+2006-12-30 : core    : Cleaned up setitem/setslice w/ hard_mask=True
+           : core    : Fixed masked_unary/binary_operations...
+					  ...to work with subclasses of MaskedArray
+2006-12-22 : core    : Optimized(?) default_/maximum_/minimum_fill_value
+           : core    : Force __new__ to not return a MaskedArray, in order to ...
+           :         ... optimize __array_finalize__
+           : core    : Add the hard_mask flag to __new__ (*[False]*)
+2006-12-19 : core    : Fixed a problem on _set_mask which prevented to set a mask to nomask
+           : extras  : Renamed compress2d to compress_rowcols
+           : extras  : Added dot
+2006-12-18 : extras  : Added compress2d and mask_rowcols
+           : extras  : moved 'average' to 'extras'
+2006-12-13 : core    : Fixed make_mask (forced filling to True)
+           : core    : Fixed ndim
+	       : core    : Fixed error message in __new__ when wrong sizes
+           : core    : Fixed the reshape function.
+		   : extras  : masked_all: set default dtype to float_
+		   : extras  : _fromnxfunctions: make sure that list are recognized
+		   : extras  : added notmasked_edges, notmasked_contiguous
+2006-12-09 : - Code reorganization: define 2 modules, core and extras
+2006-11-25 : core    : Disable copy by default
+             core    : Added keep_mask flag (to save mask when creating a ma from a ma)
+             core    : Fixed functions: empty_like
+             core    : Fixed methods: .any and .all 
+             core    : New functions: masked_all, masked_all_like
+             core    : New methods: .squeeze
+2006-11-20 : core    : fixed make_mask 
+             core    : fixed nonzero method
+2006-11-16 : core    : fixed .T
+2006-11-12 : core    : add max, min as function (not only method...)
+             core    : repr returns a name like masked_xxx, where xxx is the subclass
+2006-10-31 : core    : make sure that make_mask returns a pure ndarray.
+2006-10-30 : core    : When converted to a float, a masked singleton is ...
+                     ...transformed to nan instead of raising an exception.
+21: Use __get__ method in _arraymethods, _arithmethods, _compamethods
+18: Updated put to match the definition of numpy 1.0, deleted putmask, changed resize
+2: prevent an extra kword being sent to make_mask_none
+
+#............................................................
\ No newline at end of file

Added: trunk/scipy/sandbox/maskedarray/LICENSE
===================================================================
--- trunk/scipy/sandbox/maskedarray/LICENSE	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/LICENSE	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,24 @@
+* Copyright (c) 2006, University of Georgia and Pierre G.F. Gerard-Marchant
+* All rights reserved.
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of the Univeristy of Georgia nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file

Added: trunk/scipy/sandbox/maskedarray/README
===================================================================
--- trunk/scipy/sandbox/maskedarray/README	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/README	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1 @@
+# An alternative implementation of masked arrays in numpy
\ No newline at end of file

Added: trunk/scipy/sandbox/maskedarray/__init__.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/__init__.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/__init__.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,22 @@
+"""Masked arrays add-ons.
+
+A collection of utilities for maskedarray
+
+:author: Pierre GF Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: __init__.py 3473 2007-10-29 15:18:13Z jarrod.millman $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3473 $"
+__date__     = '$Date: 2007-10-29 08:18:13 -0700 (Mon, 29 Oct 2007) $'
+
+import core
+from core import *
+
+import extras
+from extras import *
+
+__all__ = ['core', 'extras']
+__all__ += core.__all__
+__all__ += extras.__all__

Added: trunk/scipy/sandbox/maskedarray/alternative_versions/core_alt.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/alternative_versions/core_alt.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/alternative_versions/core_alt.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,2993 @@
+# pylint: disable-msg=E1002
+"""MA: a facility for dealing with missing observations
+MA is generally used as a numpy.array look-alike.
+by Paul F. Dubois.
+
+Copyright 1999, 2000, 2001 Regents of the University of California.
+Released for unlimited redistribution.
+Adapted for numpy_core 2005 by Travis Oliphant and
+(mainly) Paul Dubois.
+
+Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
+pgmdevlist_AT_gmail_DOT_com
+Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com)
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: core_alt.py 3473 2007-10-29 15:18:13Z jarrod.millman $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3473 $"
+__date__     = '$Date: 2007-10-29 08:18:13 -0700 (Mon, 29 Oct 2007) $'
+
+__all__ = ['MAError', 'MaskType', 'MaskedArray',
+           'bool_', 'complex_', 'float_', 'int_', 'object_',
+           'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
+               'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
+               'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
+               'arctanh', 'argmax', 'argmin', 'argsort', 'around',
+               'array', 'asarray',
+           'bitwise_and', 'bitwise_or', 'bitwise_xor',
+           'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
+               'cos', 'cosh', 'count',
+           'diagonal', 'divide', 'dump', 'dumps',
+           'empty', 'empty_like', 'equal', 'exp',
+           'fabs', 'fmod', 'filled', 'floor', 'floor_divide',
+           'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
+           'ids', 'inner', 'innerproduct',
+               'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
+           'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
+               'logical_and', 'logical_not', 'logical_or', 'logical_xor',
+           'make_mask', 'make_mask_none', 'mask_or', 'masked',
+               'masked_array', 'masked_equal', 'masked_greater',
+               'masked_greater_equal', 'masked_inside', 'masked_less',
+               'masked_less_equal', 'masked_not_equal', 'masked_object',
+               'masked_outside', 'masked_print_option', 'masked_singleton',
+               'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
+               'minimum', 'multiply',
+           'negative', 'nomask', 'nonzero', 'not_equal',
+           'ones', 'outer', 'outerproduct',
+           'power', 'product', 'ptp', 'put', 'putmask',
+           'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
+               'right_shift', 'round_',
+           'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
+               'subtract', 'sum', 'swapaxes',
+           'take', 'tan', 'tanh', 'transpose', 'true_divide',
+           'var', 'where',
+           'zeros']
+
+import sys
+import types
+import cPickle
+#
+import numpy
+from numpy import bool_, complex_, float_, int_, object_, str_
+
+import numpy.core.umath as umath
+import numpy.core.fromnumeric  as fromnumeric
+from numpy.core.numeric import ndarray
+from numpy.core.fromnumeric import amax, amin
+import numpy.core.numerictypes as ntypes
+from numpy.core.numerictypes import bool_, typecodes
+from numpy.core.multiarray import dtype
+import numpy.core.numeric as numeric
+from numpy.lib.shape_base import expand_dims as n_expand_dims
+import warnings
+
+
+MaskType = bool_
+nomask = MaskType(0)
+
+divide_tolerance = 1.e-35
+numpy.seterr(all='ignore')
+
+# TODO: There's still a problem with N.add.reduce not working...
+# TODO: ...neither does N.add.accumulate
+
+#####--------------------------------------------------------------------------
+#---- --- Exceptions ---
+#####--------------------------------------------------------------------------
+class MAError(Exception):
+    "Class for MA related errors."
+    def __init__ (self, args=None):
+        "Creates an exception."
+        Exception.__init__(self,args)
+        self.args = args
+    def __str__(self):
+        "Calculates the string representation."
+        return str(self.args)
+    __repr__ = __str__
+
+#####--------------------------------------------------------------------------
+#---- --- Filling options ---
+#####--------------------------------------------------------------------------
+# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
+default_filler = {'b': True,
+                  'c' : 1.e20 + 0.0j,
+                  'f' : 1.e20,
+                  'i' : 999999,
+                  'O' : '?',
+                  'S' : 'N/A',
+                  'u' : 999999,
+                  'V' : '???',
+                  }
+max_filler = ntypes._minvals
+max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]])
+min_filler = ntypes._maxvals
+min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]])
+if 'float128' in ntypes.typeDict:
+    max_filler.update([(numpy.float128,-numeric.inf)])
+    min_filler.update([(numpy.float128, numeric.inf)])
+
+
+def default_fill_value(obj):
+    "Calculates the default fill value for an object `obj`."
+    if hasattr(obj,'dtype'):
+        defval = default_filler[obj.dtype.kind]
+    elif isinstance(obj, numeric.dtype):
+        defval = default_filler[obj.kind]
+    elif isinstance(obj, float):
+        defval = default_filler['f']
+    elif isinstance(obj, int) or isinstance(obj, long):
+        defval = default_filler['i']
+    elif isinstance(obj, str):
+        defval = default_filler['S']
+    elif isinstance(obj, complex):
+        defval = default_filler['c']
+    else:
+        defval = default_filler['O']
+    return defval
+
+def minimum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the minimum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = min_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return min_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return min_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return min_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return min_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def maximum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the maximum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = max_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return max_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return max_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return max_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return max_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def set_fill_value(a, fill_value):
+    "Sets the fill value of `a` if it is a masked array."
+    if isinstance(a, MaskedArray):
+        a.set_fill_value(fill_value)
+
+def get_fill_value(a):
+    """Returns the fill value of `a`, if any.
+    Otherwise, returns the default fill value for that type.
+    """
+    if isinstance(a, MaskedArray):
+        result = a.fill_value
+    else:
+        result = default_fill_value(a)
+    return result
+
+def common_fill_value(a, b):
+    "Returns the common fill_value of `a` and `b`, if any, or `None`."
+    t1 = get_fill_value(a)
+    t2 = get_fill_value(b)
+    if t1 == t2:
+        return t1
+    return None
+
+#................................................
+def filled(a, value = None):
+    """Returns `a` as an array with masked data replaced by `value`.
+If `value` is `None` or the special element `masked`, `get_fill_value(a)`
+is used instead.
+
+If `a` is already a contiguous numeric array, `a` itself is returned.
+
+`filled(a)` can be used to be sure that the result is numeric when passing
+an object a to other software ignorant of MA, in particular to numpy itself.
+    """
+    if hasattr(a, 'filled'):
+        return a.filled(value)
+    elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
+        return a
+    elif isinstance(a, dict):
+        return numeric.array(a, 'O')
+    else:
+        return numeric.array(a)
+
+def get_masked_subclass(*arrays):
+    """Returns the youngest subclass of MaskedArray from a list of arrays,
+ or MaskedArray. In case of siblings, the first takes over."""
+    if len(arrays) == 1:
+        arr = arrays[0]
+        if isinstance(arr, MaskedArray):
+            rcls = type(arr)
+        else:
+            rcls = MaskedArray
+    else:
+        arrcls = [type(a) for a in arrays]
+        rcls = arrcls[0]
+        if not issubclass(rcls, MaskedArray):
+            rcls = MaskedArray
+        for cls in arrcls[1:]:
+            if issubclass(cls, rcls):
+                rcls = cls
+    return rcls
+
+#####--------------------------------------------------------------------------
+#---- --- Ufuncs ---
+#####--------------------------------------------------------------------------
+ufunc_domain = {}
+ufunc_fills = {}
+
+class domain_check_interval:
+    """Defines a valid interval,
+so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`."""
+    def __init__(self, a, b):
+        "domain_check_interval(a,b)(x) = true where x < a or y > b"
+        if (a > b):
+            (a, b) = (b, a)
+        self.a = a
+        self.b = b
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.logical_or(umath.greater (x, self.b),
+                                umath.less(x, self.a))
+#............................
+class domain_tan:
+    """Defines a valid interval for the `tan` function,
+so that `domain_tan(eps) = True where `abs(cos(x)) < eps`"""
+    def __init__(self, eps):
+        "domain_tan(eps) = true where abs(cos(x)) < eps)"
+        self.eps = eps
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(umath.absolute(umath.cos(x)), self.eps)
+#............................
+class domain_safe_divide:
+    """defines a domain for safe division."""
+    def __init__ (self, tolerance=divide_tolerance):
+        self.tolerance = tolerance
+    def __call__ (self, a, b):
+        return umath.absolute(a) * self.tolerance >= umath.absolute(b)
+#............................
+class domain_greater:
+    "domain_greater(v)(x) = true where x <= v"
+    def __init__(self, critical_value):
+        "domain_greater(v)(x) = true where x <= v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less_equal(x, self.critical_value)
+#............................
+class domain_greater_equal:
+    "domain_greater_equal(v)(x) = true where x < v"
+    def __init__(self, critical_value):
+        "domain_greater_equal(v)(x) = true where x < v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(x, self.critical_value)
+#..............................................................................
+class masked_unary_operation:
+    """Defines masked version of unary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fill` : Default filling value *[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mufunc, fill=0, domain=None):
+        """ masked_unary_operation(aufunc, fill=0, domain=None)
+            aufunc(fill) must be defined
+            self(x) returns aufunc(x)
+            with masked values where domain(x) is true or getmask(x) is true.
+        """
+        self.f = mufunc
+        self.fill = fill
+        self.domain = domain
+        self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
+        self.__name__ = getattr(mufunc, "__name__", str(mufunc))
+        ufunc_domain[mufunc] = domain
+        ufunc_fills[mufunc] = fill
+    #
+    def __call__ (self, a, *args, **kwargs):
+        "Execute the call behavior."
+# numeric tries to return scalars rather than arrays when given scalars.
+        m = getmask(a)
+        d1 = filled(a, self.fill)
+        if self.domain is not None:
+            m = mask_or(m, numeric.asarray(self.domain(d1)))
+        # Take care of the masked singletong first ...
+        if m.ndim == 0 and m:
+            return masked
+        # Get the result....
+        if isinstance(a, MaskedArray):
+            result = self.f(d1, *args, **kwargs).view(type(a))
+        else:
+            result = self.f(d1, *args, **kwargs).view(MaskedArray)
+        # Fix the mask if we don't have a scalar
+        if result.ndim > 0:
+            result._mask = m
+        return result
+    #
+    def __str__ (self):
+        return "Masked version of %s. [Invalid values are masked]" % str(self.f)
+#..............................................................................
+class masked_binary_operation:
+    """Defines masked version of binary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mbfunc, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = mbfunc
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
+        self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
+        ufunc_domain[mbfunc] = None
+        ufunc_fills[mbfunc] = (fillx, filly)
+    #
+    def __call__ (self, a, b, *args, **kwargs):
+        "Execute the call behavior."
+        m = mask_or(getmask(a), getmask(b))
+        if (not m.ndim) and m:
+            return masked
+        result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b))
+        if result.ndim > 0:
+            result.unshare_mask()
+            result._masklayer |= m
+        return result
+    #
+    def reduce (self, target, axis=0, dtype=None):
+        """Reduces `target` along the given `axis`."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = MaskedArray
+        m = getmask(target)
+        t = filled(target, self.filly)
+        if t.shape == ():
+            t = t.reshape(1)
+            if m is not nomask:
+                m = make_mask(m, copy=1)
+                m.shape = (1,)
+        if m is nomask:
+            return self.f.reduce(t, axis).view(tclass)
+        t = t.view(tclass)
+        t._mask = m
+        # XXX: "or t.dtype" below is a workaround for what appears
+        # XXX: to be a bug in reduce.
+        tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype)
+        mr = umath.logical_and.reduce(m, axis)
+        tr = tr.view(tclass)
+        if mr.ndim > 0:
+            tr._mask = mr
+            return tr
+        elif mr:
+            return masked
+        return tr
+
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = umath.logical_or.outer(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        rcls = get_masked_subclass(a,b)
+        d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
+        if d.ndim > 0:
+            d._mask = m
+        return d
+
+    def accumulate (self, target, axis=0):
+        """Accumulates `target` along `axis` after filling with y fill value."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = masked_array
+        t = filled(target, self.filly)
+        return self.f.accumulate(t, axis).view(tclass)
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+#..............................................................................
+class domained_binary_operation:
+    """Defines binary operations that have a domain, like divide.
+
+These are complicated so they are a separate class.
+They have no reduce, outer or accumulate.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, dbfunc, domain, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = dbfunc
+        self.domain = domain
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
+        self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
+        ufunc_domain[dbfunc] = domain
+        ufunc_fills[dbfunc] = (fillx, filly)
+
+    def __call__(self, a, b):
+        "Execute the call behavior."
+        ma = getmask(a)
+        mb = getmask(b)
+        d1 = filled(a, self.fillx)
+        d2 = filled(b, self.filly)
+        t = numeric.asarray(self.domain(d1, d2))
+
+        if fromnumeric.sometrue(t, None):
+            d2 = numeric.where(t, self.filly, d2)
+            mb = mask_or(mb, t)
+        m = mask_or(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        result =  self.f(d1, d2).view(get_masked_subclass(a,b))
+        if result.ndim > 0:
+            result._mask = m
+        return result
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+
+#..............................................................................
+# Unary ufuncs
+exp = masked_unary_operation(umath.exp)
+conjugate = masked_unary_operation(umath.conjugate)
+sin = masked_unary_operation(umath.sin)
+cos = masked_unary_operation(umath.cos)
+tan = masked_unary_operation(umath.tan)
+arctan = masked_unary_operation(umath.arctan)
+arcsinh = masked_unary_operation(umath.arcsinh)
+sinh = masked_unary_operation(umath.sinh)
+cosh = masked_unary_operation(umath.cosh)
+tanh = masked_unary_operation(umath.tanh)
+abs = absolute = masked_unary_operation(umath.absolute)
+fabs = masked_unary_operation(umath.fabs)
+negative = masked_unary_operation(umath.negative)
+floor = masked_unary_operation(umath.floor)
+ceil = masked_unary_operation(umath.ceil)
+around = masked_unary_operation(fromnumeric.round_)
+logical_not = masked_unary_operation(umath.logical_not)
+# Domained unary ufuncs
+sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0))
+log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0))
+log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0))
+tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35))
+arcsin = masked_unary_operation(umath.arcsin, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccos = masked_unary_operation(umath.arccos, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0))
+arctanh = masked_unary_operation(umath.arctanh, 0.0,
+                                 domain_check_interval(-1.0+1e-15, 1.0-1e-15))
+# Binary ufuncs
+add = masked_binary_operation(umath.add)
+subtract = masked_binary_operation(umath.subtract)
+multiply = masked_binary_operation(umath.multiply, 1, 1)
+arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0)
+equal = masked_binary_operation(umath.equal)
+equal.reduce = None
+not_equal = masked_binary_operation(umath.not_equal)
+not_equal.reduce = None
+less_equal = masked_binary_operation(umath.less_equal)
+less_equal.reduce = None
+greater_equal = masked_binary_operation(umath.greater_equal)
+greater_equal.reduce = None
+less = masked_binary_operation(umath.less)
+less.reduce = None
+greater = masked_binary_operation(umath.greater)
+greater.reduce = None
+logical_and = masked_binary_operation(umath.logical_and)
+alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce
+logical_or = masked_binary_operation(umath.logical_or)
+sometrue = logical_or.reduce
+logical_xor = masked_binary_operation(umath.logical_xor)
+bitwise_and = masked_binary_operation(umath.bitwise_and)
+bitwise_or = masked_binary_operation(umath.bitwise_or)
+bitwise_xor = masked_binary_operation(umath.bitwise_xor)
+hypot = masked_binary_operation(umath.hypot)
+# Domained binary ufuncs
+divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1)
+true_divide = domained_binary_operation(umath.true_divide,
+                                        domain_safe_divide(), 0, 1)
+floor_divide = domained_binary_operation(umath.floor_divide,
+                                         domain_safe_divide(), 0, 1)
+remainder = domained_binary_operation(umath.remainder,
+                                      domain_safe_divide(), 0, 1)
+fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Mask creation functions ---
+#####--------------------------------------------------------------------------
+def getmask(a):
+    """Returns the mask of `a`, if any, or `nomask`.
+Returns `nomask` if `a` is not a masked array.
+To get an array for sure use getmaskarray."""
+    if hasattr(a, "_mask"):
+        return a._mask
+    else:
+        return nomask
+
+def getmaskarray(a):
+    """Returns the mask of `a`, if any.
+Otherwise, returns an array of `False`, with the same shape as `a`.
+    """
+    m = getmask(a)
+    if m is nomask:
+        return make_mask_none(fromnumeric.shape(a))
+    else:
+        return m
+
+def is_mask(m):
+    """Returns `True` if `m` is a legal mask.
+Does not check contents, only type.
+    """
+    try:
+        return m.dtype.type is MaskType
+    except AttributeError:
+        return False
+#
+def make_mask(m, copy=False, small_mask=True, flag=None):
+    """make_mask(m, copy=0, small_mask=0)
+Returns `m` as a mask, creating a copy if necessary or requested.
+The function can accept any sequence of integers or `nomask`.
+Does not check that contents must be 0s and 1s.
+If `small_mask=True`, returns `nomask` if `m` contains no true elements.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+    """
+    if flag is not None:
+        warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                      DeprecationWarning)
+        small_mask = flag
+    if m is nomask:
+        return nomask
+    elif isinstance(m, ndarray):
+        m = filled(m, True)
+        if m.dtype.type is MaskType:
+            if copy:
+                result = numeric.array(m, dtype=MaskType, copy=copy)
+            else:
+                result = m
+        else:
+            result = numeric.array(m, dtype=MaskType)
+    else:
+        result = numeric.array(filled(m, True), dtype=MaskType)
+    # Bas les masques !
+    if small_mask and not result.any():
+        return nomask
+    else:
+        return result
+
+def make_mask_none(s):
+    "Returns a mask of shape `s`, filled with `False`."
+    result = numeric.zeros(s, dtype=MaskType)
+    return result
+
+def mask_or (m1, m2, copy=False, small_mask=True):
+    """Returns the combination of two masks `m1` and `m2`.
+The masks are combined with the `logical_or` operator, treating `nomask` as false.
+The result may equal m1 or m2 if the other is nomask.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+     """
+    if m1 is nomask:
+        return make_mask(m2, copy=copy, small_mask=small_mask)
+    if m2 is nomask:
+        return make_mask(m1, copy=copy, small_mask=small_mask)
+    if m1 is m2 and is_mask(m1):
+        return m1
+    return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
+
+#####--------------------------------------------------------------------------
+#--- --- Masking functions ---
+#####--------------------------------------------------------------------------
+def masked_where(condition, x, copy=True):
+    """Returns `x` as an array masked where `condition` is true.
+Masked values of `x` or `condition` are kept.
+
+:Parameters:
+    - `condition` (ndarray) : Masking condition.
+    - `x` (ndarray) : Array to mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    """
+    cm = filled(condition,1)
+    if isinstance(x,MaskedArray):
+        m = mask_or(x._mask, cm)
+        return x.__class__(x._data, mask=m, copy=copy)
+    else:
+        return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm)
+
+def masked_greater(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x > value)``."
+    return masked_where(greater(x, value), x, copy=copy)
+
+def masked_greater_equal(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x >= value)``."
+    return masked_where(greater_equal(x, value), x, copy=copy)
+
+def masked_less(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x < value)``."
+    return masked_where(less(x, value), x, copy=copy)
+
+def masked_less_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x <= value)``."
+    return masked_where(less_equal(x, value), x, copy=copy)
+
+def masked_not_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x != value)``."
+    return masked_where((x != value), x, copy=copy)
+
+#
+def masked_equal(x, value, copy=True):
+    """Shortcut to `masked_where`, with ``condition = (x == value)``.
+For floating point, consider `masked_values(x, value)` instead.
+    """
+    return masked_where((x == value), x, copy=copy)
+#    d = filled(x, 0)
+#    c = umath.equal(d, value)
+#    m = mask_or(c, getmask(x))
+#    return array(d, mask=m, copy=copy)
+
+def masked_inside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x inside
+the interval `[v1,v2]` ``(v1 <= x <= v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf >= v1) & (xf <= v2)
+    return masked_where(condition, x, copy=copy)
+
+def masked_outside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x outside
+the interval `[v1,v2]` ``(x < v1)|(x > v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf < v1) | (xf > v2)
+    return masked_where(condition, x, copy=copy)
+
+#
+def masked_object(x, value, copy=True):
+    """Masks the array `x` where the data are exactly equal to `value`.
+This function is suitable only for `object` arrays: for floating point,
+please use `masked_values` instead.
+The mask is set to `nomask` if posible.
+
+:parameter copy (Boolean, *[True]*):  Returns a copy of `x` if true. """
+    if isMaskedArray(x):
+        condition = umath.equal(x._data, value)
+        mask = x._mask
+    else:
+        condition = umath.equal(fromnumeric.asarray(x), value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(x, mask=mask, copy=copy, fill_value=value)
+
+def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
+    """Masks the array `x` where the data are approximately equal to `value`
+(that is, ``abs(x - value) <= atol+rtol*abs(value)``).
+Suitable only for floating points. For integers, please use `masked_equal`.
+The mask is set to `nomask` if posible.
+
+:Parameters:
+    - `rtol` (Float, *[1e-5]*): Tolerance parameter.
+    - `atol` (Float, *[1e-8]*): Tolerance parameter.
+    - `copy` (boolean, *[False]*) : Returns a copy of `x` if True.
+    """
+    abs = umath.absolute
+    xnew = filled(x, value)
+    if issubclass(xnew.dtype.type, numeric.floating):
+        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+        try:
+            mask = x._mask
+        except AttributeError:
+            mask = nomask
+    else:
+        condition = umath.equal(xnew, value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
+
+#####--------------------------------------------------------------------------
+#---- --- Printing options ---
+#####--------------------------------------------------------------------------
+class _MaskedPrintOption:
+    """Handles the string used to represent missing data in a masked array."""
+    def __init__ (self, display):
+        "Creates the masked_print_option object."
+        self._display = display
+        self._enabled = True
+
+    def display(self):
+        "Displays the string to print for masked values."
+        return self._display
+
+    def set_display (self, s):
+        "Sets the string to print for masked values."
+        self._display = s
+
+    def enabled(self):
+        "Is the use of the display value enabled?"
+        return self._enabled
+
+    def enable(self, small_mask=1):
+        "Set the enabling small_mask to `small_mask`."
+        self._enabled = small_mask
+
+    def __str__ (self):
+        return str(self._display)
+
+    __repr__ = __str__
+
+#if you single index into a masked location you get this object.
+masked_print_option = _MaskedPrintOption('--')
+
+#####--------------------------------------------------------------------------
+#---- --- MaskedArray class ---
+#####--------------------------------------------------------------------------
+#def _getoptions(a_out, a_in):
+#    "Copies standards options of a_in to a_out."
+#    for att in [']
+class _mathmethod(object):
+    """Defines a wrapper for arithmetic methods.
+Instead of directly calling a ufunc, the corresponding method of  the `array._data`
+object is called instead.
+    """
+    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+        """
+:Parameters:
+    - `methodname` (String) : Method name.
+    - `fill_self` (Float *[0]*) : Fill value for the instance.
+    - `fill_other` (Float *[0]*) : Fill value for the target.
+    - `domain` (Domain object *[None]*) : Domain of non-validity.
+        """
+        self.methodname = methodname
+        self.fill_self = fill_self
+        self.fill_other = fill_other
+        self.domain = domain
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self.methodname).__doc__
+        except:
+            return getattr(ndarray, self.methodname).__doc__
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__ (self, other, *args):
+        "Execute the call behavior."
+        instance = self.obj
+        m_self = instance._mask
+        m_other = getmask(other)
+        base = instance.filled(self.fill_self)
+        target = filled(other, self.fill_other)
+        if self.domain is not None:
+            # We need to force the domain to a ndarray only.
+            if self.fill_other > self.fill_self:
+                domain = self.domain(base, target)
+            else:
+                domain = self.domain(target, base)
+            if domain.any():
+                #If `other` is a subclass of ndarray, `filled` must have the
+                # same subclass, else we'll lose some info.
+                #The easiest then is to fill `target` instead of creating
+                # a pure ndarray.
+                #Oh, and we better make a copy!
+                if isinstance(other, ndarray):
+                    # We don't want to modify other: let's copy target, then
+                    target = target.copy()
+                    target[fromnumeric.asarray(domain)] = self.fill_other
+                else:
+                    target = numeric.where(fromnumeric.asarray(domain),
+                                           self.fill_other, target)
+                m_other = mask_or(m_other, domain)
+        m = mask_or(m_self, m_other)
+        method = getattr(base, self.methodname)
+        result = method(target, *args).view(type(instance))
+        try:
+            result._mask = m
+        except AttributeError:
+            if m:
+                result = masked
+        return result
+#...............................................................................
+class _arraymethod(object):
+    """Defines a wrapper for basic array methods.
+Upon call, returns a masked array, where the new `_data` array is the output
+of the corresponding method called on the original `_data`.
+
+If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+If `onmask` is False, the new mask is just a reference to the initial mask.
+
+:Parameters:
+    `funcname` : String
+        Name of the function to apply on data.
+    `onmask` : Boolean *[True]*
+        Whether the mask must be processed also (True) or left alone (False).
+    """
+    def __init__(self, funcname, onmask=True):
+        self._name = funcname
+        self._onmask = onmask
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        methdoc = getattr(ndarray, self._name, None)
+        methdoc = getattr(numpy, self._name, methdoc)
+#        methdoc = getattr(MaskedArray, self._name, methdoc)
+        if methdoc is not None:
+            return methdoc.__doc__
+#        try:
+#            return getattr(MaskedArray, self._name).__doc__
+#        except:
+#            try:
+#                return getattr(numpy, self._name).__doc__
+#            except:
+#                return getattr(ndarray, self._name).__doc
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__(self, *args, **params):
+        methodname = self._name
+        data = self.obj._data
+        mask = self.obj._mask
+        cls = type(self.obj)
+        result = getattr(data, methodname)(*args, **params).view(cls)
+        if result.ndim:
+            if not self._onmask:
+                result._mask = mask
+            elif mask is not nomask:
+                result.__setmask__(getattr(mask, methodname)(*args, **params))
+        return result
+#..........................................................
+
+class flatiter(object):
+    "Defines an interator."
+    def __init__(self, ma):
+        self.ma = ma
+        self.ma_iter = numpy.asarray(ma).flat
+
+        if ma._mask is nomask:
+            self.maskiter = None
+        else:
+            self.maskiter = ma._mask.flat
+
+    def __iter__(self):
+        return self
+
+    ### This won't work is ravel makes a copy
+    def __setitem__(self, index, value):
+        a = self.ma.ravel()
+        a[index] = value
+
+    def next(self):
+        d = self.ma_iter.next()
+        if self.maskiter is not None and self.maskiter.next():
+            d = masked
+        return d
+
+
+class MaskedArray(numeric.ndarray):
+    """Arrays with possibly masked values.
+Masked values of True exclude the corresponding element from any computation.
+
+Construction:
+    x = array(data, dtype=None, copy=True, order=False,
+              mask = nomask, fill_value=None, small_mask=True)
+
+If copy=False, every effort is made not to copy the data:
+If `data` is a MaskedArray, and argument mask=nomask, then the candidate data
+is `data._data` and the mask used is `data._mask`.
+If `data` is a numeric array, it is used as the candidate raw data.
+If `dtype` is not None and is different from data.dtype.char then a data copy is required.
+Otherwise, the candidate is used.
+
+If a data copy is required, the raw (unmasked) data stored is the result of:
+numeric.array(data, dtype=dtype.char, copy=copy)
+
+If `mask` is `nomask` there are no masked values.
+Otherwise mask must be convertible to an array of booleans with the same shape as x.
+If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`.
+Otherwise, the mask is not compressed.
+
+fill_value is used to fill in masked values when necessary, such as when
+printing and in method/function filled().
+The fill_value is not used for computation within this module.
+    """
+    __array_priority__ = 10.1
+    _defaultmask = nomask
+    _defaulthardmask = False
+    _baseclass =  numeric.ndarray
+    def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
+                keep_mask=True, small_mask=True, hard_mask=False, flag=None,
+                **options):
+        """array(data, dtype=None, copy=True, mask=nomask, fill_value=None)
+
+If `data` is already a ndarray, its dtype becomes the default value of dtype.
+        """
+        if flag is not None:
+            warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                          DeprecationWarning)
+            small_mask = flag
+        # Process data............
+        _data = numeric.array(data, dtype=dtype, copy=copy, subok=True)
+        _baseclass = getattr(_data, '_baseclass', type(_data))
+        _data = _data.view(cls)
+        # Pricess mask ...........
+        # Backwards compat
+        if hasattr(data,'_mask') and not isinstance(data, ndarray):
+            _data._mask = data._mask
+            _data._sharedmask = True
+        if mask is nomask:
+            if _data._hasmasked:
+                if copy:
+                    _data._masklayer = data._masklayer.copy()
+                    _data._sharedmask = False
+                if not keep_mask:
+                    _data._masklayer.flat = False
+                    _data._hasmasked = False
+#                else;
+#                    _data._mask = data._mask
+        else:
+            mask = numeric.array(mask, dtype=MaskType, copy=copy)
+            if mask.shape != _data.shape:
+                (nd, nm) = (_data.size, mask.size)
+                if nm == 1:
+                    mask = numeric.resize(mask, _data.shape)
+                elif nm == nd:
+                    mask = fromnumeric.reshape(mask, _data.shape)
+                else:
+                    msg = "Mask and data not compatible: data size is %i, "+\
+                          "mask size is %i."
+                    raise MAError, msg % (nd, nm)
+            if not (_data._hasmasked and keep_mask):
+                _data._masklayer = mask
+                _data._hasmasked = True
+                _data._sharedmask = True
+            else:
+                _data._masklayer = _data._masklayer.copy()
+                _data._mask.__ior__(mask)
+                _data._sharedmask = False
+                _data._hasmasked = True
+        # Process extra options ..
+        # Update fille_value
+        _data._fill_value = getattr(data, '_fill_value', fill_value)
+        if _data._fill_value is None:
+            _data._fill_value = default_fill_value(_data)
+        _data._hardmask = hard_mask
+        _data._smallmask = small_mask
+        _data._baseclass = _baseclass
+        return _data
+    #........................
+    def __array_finalize__(self,obj):
+        """Finalizes the masked array.
+        """
+        # Finalize mask ...............
+        self._masklayer = getattr(obj, '_masklayer', nomask)
+        self._hasmasked = getattr(obj, '_hasmasked', False)
+        if self._masklayer is nomask:
+            self._masklayer = numeric.zeros(obj.shape, dtype=MaskType)
+            self._hasmasked = False
+#        if not self.shape:
+#            self._mask.shape = obj.shape
+#        else:
+#            self._mask.shape = self.shape
+        if self._hasmasked:
+            self._masklayer.shape = self.shape
+#            except ValueError:
+#                self._mask = nomask
+        # Get the remaining options ...
+        self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask)
+        self._smallmask = getattr(obj, '_smallmask', True)
+        self._sharedmask = True
+        self._baseclass = getattr(obj, '_baseclass', type(obj))
+        self._fill_value = getattr(obj, '_fill_value', None)
+        return
+    #..................................
+    def __array_wrap__(self, obj, context=None):
+        """Special hook for ufuncs.
+Wraps the numpy array and sets the mask according to context.
+        """
+        result = obj.view(type(self))
+        result._masklayer = self._masklayer.copy()
+        result._hasmasked = self._hasmasked
+#        result._hasmasked = result._masklayer.any()
+        #..........
+        if context is not None:
+            (func, args, out_index) = context
+            m = reduce(mask_or, [getmask(arg) for arg in args])
+            # Get domain mask
+            domain = ufunc_domain.get(func, None)
+            if domain is not None:
+                if len(args) > 2:
+                    d = reduce(domain, args)
+                else:
+                    d = domain(*args)
+                if m is nomask:
+                    if d is not nomask:
+                        result._masklayer.flat = d
+                        result._hasmasked = True
+                else:
+                    result._masklayer |= d
+                    result._hasmasked |= d.any()
+            else:
+                result._masklayer.__ior__(m)
+                result._hasmasked = m.any()
+        if result._masklayer.size == 1 and result._masklayer.item():
+            return masked
+        #....
+        result._fill_value = self._fill_value
+        result._hardmask = self._hardmask
+        result._smallmask = self._smallmask
+        result._baseclass = self._baseclass
+        return result
+    #.............................................
+    def __getitem__(self, indx):
+        """x.__getitem__(y) <==> x[y]
+Returns the item described by i. Not a copy as in previous versions.
+        """
+        # This test is useful, but we should keep things light...
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        # super() can't work here if the underlying data is a matrix...
+        result = (self._data).__getitem__(indx)
+        m = self._masklayer[indx]
+        if hasattr(result, 'shape') and len(result.shape) > 0:
+            # Not a scalar: make sure that dout is a MA
+            result = result.view(type(self))
+            result.__setmask__(m)
+        elif not m.shape and m:
+            return masked
+        return result
+    #........................
+    def __setitem__(self, indx, value):
+        """x.__setitem__(i, y) <==> x[i]=y
+Sets item described by index. If value is masked, masks those locations.
+        """
+        if self is masked:
+            raise MAError, 'Cannot alter the masked element.'
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        #....
+        if value is masked:
+#            if self._sharedmask:
+#                slef.unshare_mask()
+            self._masklayer[indx] = True
+            self._hasmasked = True
+            return
+        #....
+        dval = numeric.asarray(value).astype(self.dtype)
+        valmask = getmask(value)
+
+        if not self._hasmasked:
+            if valmask is not nomask:
+                self._masklayer[indx] = valmask
+                self._hasmasked = True
+        elif not self._hardmask:
+            self.unshare_mask()
+            if valmask is nomask:
+                self._masklayer[indx] = False
+                self._hasmasked = self._masklayer.any()
+            else:
+                self._masklayer[indx] = valmask
+                self._hasmasked = True
+        elif hasattr(indx, 'dtype') and (indx.dtype==bool_):
+            indx = indx * umath.logical_not(self._masklayer)
+#        elif isinstance(index, int):
+        else:
+            mindx = mask_or(self._masklayer[indx], valmask, copy=True)
+            dindx = self._data[indx]
+            if dindx.size > 1:
+                dindx[~mindx] = dval
+            elif mindx is nomask:
+                dindx = dval
+            dval = dindx
+            self._masklayer[indx] = mindx
+        # Set data ..........
+        #dval = filled(value).astype(self.dtype)
+        ndarray.__setitem__(self._data,indx,dval)
+#        #.....
+#        if m is nomask or not m.any():
+#            self._mask = nomask
+#        else:
+#            self._mask = m
+    #............................................
+    def __getslice__(self, i, j):
+        """x.__getslice__(i, j) <==> x[i:j]
+Returns the slice described by i, j.
+The use of negative indices is not supported."""
+        return self.__getitem__(slice(i,j))
+    #........................
+    def __setslice__(self, i, j, value):
+        """x.__setslice__(i, j, value) <==> x[i:j]=value
+Sets a slice i:j to `value`.
+If `value` is masked, masks those locations."""
+        self.__setitem__(slice(i,j), value)
+    #............................................
+    def __setmask__(self, mask):
+        newmask = make_mask(mask, copy=False, small_mask=self._smallmask)
+#        self.unshare_mask()
+        if self._hardmask:
+            if newmask is not nomask:
+                self.unshare_mask()
+                self._masklayer.__ior__(newmask)
+                self._hasmasked = True
+        else:
+#            if self._hasmasked and (mask is nomask):
+#                self.unshare_mask()
+            self._masklayer.flat = newmask
+            self._hasmasked = newmask.any()
+        if self._masklayer.shape:
+            self._masklayer.shape = self.shape
+    _set_mask = __setmask__
+
+    def _get_mask(self):
+        """Returns the current mask."""
+        if not self._hasmasked and self._smallmask:
+            return nomask
+        return self._masklayer
+
+#    def _set_mask(self, mask):
+#        """Sets the mask to `mask`."""
+#        mask = make_mask(mask, copy=False, small_mask=self._smallmask)
+#        if mask is not nomask:
+#            if mask.size != self.size:
+#                raise ValueError, "Inconsistent shape between data and mask!"
+#            if mask.shape != self.shape:
+#                mask.shape = self.shape
+#            self._mask = mask
+#        else:
+#            self._mask = nomask
+    mask = _mask = property(fget=_get_mask, fset=__setmask__, doc="Mask")
+    #............................................
+    def harden_mask(self):
+        "Forces the mask to hard."
+        self._hardmask = True
+
+    def soften_mask(self):
+        "Forces the mask to soft."
+        self._hardmask = False
+
+    def unshare_mask(self):
+        if self._sharedmask:
+            self._masklayer = self._masklayer.copy()
+            self._sharedmask = False
+
+    #............................................
+    def _get_data(self):
+        "Returns the current data (as a view of the original underlying data)>"
+        return self.view(self._baseclass)
+    _data = property(fget=_get_data)
+    #............................................
+    def _get_flat(self):
+        """Calculates the flat value.
+        """
+        return flatiter(self)
+    #
+    def _set_flat (self, value):
+        "x.flat = value"
+        y = self.ravel()
+        y[:] = value
+    #
+    flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version")
+    #............................................
+    def get_fill_value(self):
+        "Returns the filling value."
+        if self._fill_value is None:
+            self._fill_value = default_fill_value(self)
+        return self._fill_value
+
+    def set_fill_value(self, value=None):
+        """Sets the filling value to `value`.
+If None, uses the default, based on the data type."""
+        if value is None:
+            value = default_fill_value(self)
+        self._fill_value = value
+
+    fill_value = property(fget=get_fill_value, fset=set_fill_value,
+                          doc="Filling value")
+
+    def filled(self, fill_value=None):
+        """Returns an array of the same class as `_data`,
+ with masked values filled with `fill_value`.
+Subclassing is preserved.
+
+If `fill_value` is None, uses self.fill_value.
+        """
+        m = self._mask
+        if m is nomask:
+            return self._data
+        #
+        if fill_value is None:
+            fill_value = self.fill_value
+        #
+        if self is masked_singleton:
+            result = numeric.asanyarray(fill_value)
+        else:
+            result = self._data.copy()
+            try:
+                result[m] = fill_value
+            except (TypeError, AttributeError):
+                fill_value = numeric.array(fill_value, dtype=object)
+                d = result.astype(object)
+                result = fromnumeric.choose(m, (d, fill_value))
+            except IndexError:
+                #ok, if scalar
+                if self._data.shape:
+                    raise
+                elif m:
+                    result = numeric.array(fill_value, dtype=self.dtype)
+                else:
+                    result = self._data
+        return result
+
+    def compressed(self):
+        "A 1-D array of all the non-masked data."
+        d = self.ravel()
+        if self._mask is nomask:
+            return d
+        else:
+            return d[numeric.logical_not(d._mask)]
+    #............................................
+    def __str__(self):
+        """x.__str__() <==> str(x)
+Calculates the string representation, using masked for fill if it is enabled.
+Otherwise, fills with fill value.
+        """
+        if masked_print_option.enabled():
+            f = masked_print_option
+            if self is masked:
+                return str(f)
+            m = self._mask
+            if m is nomask:
+                res = self._data
+            else:
+                if m.shape == () and m:
+                    return str(f)
+                # convert to object array to make filled work
+#CHECK: the two lines below seem more robust than the self._data.astype
+#                res = numeric.empty(self._data.shape, object_)
+#                numeric.putmask(res,~m,self._data)
+                res = self._data.astype("|O8")
+                res[m] = f
+        else:
+            res = self.filled(self.fill_value)
+        return str(res)
+
+    def __repr__(self):
+        """x.__repr__() <==> repr(x)
+Calculates the repr representation, using masked for fill if it is enabled.
+Otherwise fill with fill value.
+        """
+        with_mask = """\
+masked_%(name)s(data =
+ %(data)s,
+      mask =
+ %(mask)s,
+      fill_value=%(fill)s)
+"""
+        with_mask1 = """\
+masked_%(name)s(data = %(data)s,
+      mask = %(mask)s,
+      fill_value=%(fill)s)
+"""
+        n = len(self.shape)
+        name = repr(self._data).split('(')[0]
+        if n <= 1:
+            return with_mask1 % {
+                'name': name,
+                'data': str(self),
+                'mask': str(self._mask),
+                'fill': str(self.fill_value),
+                }
+        return with_mask % {
+            'name': name,
+            'data': str(self),
+            'mask': str(self._mask),
+            'fill': str(self.fill_value),
+            }
+    #............................................
+    def __iadd__(self, other):
+        "Adds other to self in place."
+        ndarray.__iadd__(self._data,other)
+        m = getmask(other)
+        if m is not nomask:
+            self._masklayer += m
+            self._hasmasked = True
+#        if m is not nomask:
+#            self._masklayer |= m
+#            self._hasmasked = True
+        return self
+    #....
+    def __isub__(self, other):
+        "Subtracts other from self in place."
+        ndarray.__isub__(self._data,other)
+        m = getmask(other)
+#        if not self._hasmasked:
+#            self._masklayer.flat = m
+#        elif m is not nomask:
+#            self._masklayer += m
+        if m is not nomask:
+            self._masklayer += m
+            self._hasmasked = True
+        return self
+    #....
+    def __imul__(self, other):
+        "Multiplies self by other in place."
+        ndarray.__imul__(self._data,other)
+        m = getmask(other)
+#        if not self._hasmasked:
+#            self._masklayer.flat = m
+#        elif m is not nomask:
+#            self._masklayer += m
+        if m is not nomask:
+            self._masklayer += m
+            self._hasmasked = True
+        return self
+    #....
+    def __idiv__(self, other):
+        "Divides self by other in place."
+        dom_mask = domain_safe_divide().__call__(self, filled(other,1))
+        other_mask = getmask(other)
+        new_mask = mask_or(other_mask, dom_mask)
+        ndarray.__idiv__(self._data, other)
+        self._mask = mask_or(self._mask, new_mask)
+        return self
+    #....
+    __add__ = _mathmethod('__add__')
+    __radd__ = _mathmethod('__add__')
+    __sub__ = _mathmethod('__sub__')
+    __rsub__ = _mathmethod('__rsub__')
+    __pow__ = _mathmethod('__pow__')
+    __mul__ = _mathmethod('__mul__', 1, 1)
+    __rmul__ = _mathmethod('__mul__', 1, 1)
+    __div__ = _mathmethod('__div__', 0, 1, domain_safe_divide())
+    __rdiv__ = _mathmethod('__rdiv__', 1, 0, domain_safe_divide())
+    __truediv__ = _mathmethod('__truediv__', 0, 1, domain_safe_divide())
+    __rtruediv__ = _mathmethod('__rtruediv__', 1, 0, domain_safe_divide())
+    __floordiv__ = _mathmethod('__floordiv__', 0, 1, domain_safe_divide())
+    __rfloordiv__ = _mathmethod('__rfloordiv__', 1, 0, domain_safe_divide())
+    __eq__ = _mathmethod('__eq__')
+    __ne__ = _mathmethod('__ne__')
+    __le__ = _mathmethod('__le__')
+    __lt__ = _mathmethod('__lt__')
+    __ge__ = _mathmethod('__ge__')
+    __gt__ = _mathmethod('__gt__')
+
+    #............................................
+    def __float__(self):
+        "Converts self to float."
+        if self._mask is not nomask:
+            warnings.warn("Warning: converting a masked element to nan.")
+            return numpy.nan
+            #raise MAError, 'Cannot convert masked element to a Python float.'
+        return float(self.item())
+
+    def __int__(self):
+        "Converts self to int."
+        if self._mask is not nomask:
+            raise MAError, 'Cannot convert masked element to a Python int.'
+        return int(self.item())
+    #............................................
+    def count(self, axis=None):
+        """Counts the non-masked elements of the array along a given axis,
+and returns a masked array where the mask is True where all data are masked.
+If `axis` is None, counts all the non-masked elements, and returns either a
+scalar or the masked singleton."""
+        m = self._mask
+        s = self.shape
+        ls = len(s)
+        if m is nomask:
+            if ls == 0:
+                return 1
+            if ls == 1:
+                return s[0]
+            if axis is None:
+                return self.size
+            else:
+                n = s[axis]
+                t = list(s)
+                del t[axis]
+                return numeric.ones(t) * n
+        n1 = fromnumeric.size(m, axis)
+        n2 = m.astype(int_).sum(axis)
+        if axis is None:
+            return (n1-n2)
+        else:
+            return masked_array(n1 - n2)
+    #............................................
+#    def _get_shape(self):
+#        "Returns the current shape."
+#        return self._data.shape
+#    #
+#    def _set_shape (self, newshape):
+#        "Sets the array's shape."
+#        self._data.shape = newshape
+#        if self._mask is not nomask:
+#            #self._mask = self._mask.copy()
+#            self._mask.shape = newshape
+#    #
+#    shape = property(fget=_get_shape, fset=_set_shape,
+#                     doc="Shape of the array, as a tuple.")
+    #
+    def reshape (self, *s):
+        """Reshapes the array to shape s.
+Returns a new masked array.
+If you want to modify the shape in place, please use `a.shape = s`"""
+        # TODO: Do we keep super, or reshape _data and take a view ?
+        result = super(MaskedArray, self).reshape(*s)
+        result._masklayer = self._masklayer.reshape(*s)
+        return result
+    #
+    repeat = _arraymethod('repeat')
+    #
+    def resize(self, newshape, refcheck=True, order=False):
+        """Attempts to modify size and shape of self inplace.
+        The array must own its own memory and not be referenced by other arrays.
+        Returns None.
+        """
+        try:
+            self._data.resize(newshape, refcheck, order)
+            if self.mask is not nomask:
+                self._mask.resize(newshape, refcheck, order)
+        except ValueError:
+            raise ValueError("Cannot resize an array that has been referenced "
+                             "or is referencing another array in this way.\n"
+                             "Use the resize function.")
+        return None
+    #
+    flatten = _arraymethod('flatten')
+    #
+    def put(self, indices, values, mode='raise'):
+        """Sets storage-indexed locations to corresponding values.
+a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
+`values` can be scalar or an array shorter than indices, and it will be repeated,
+if necessary.
+If `values` has some masked values, the initial mask is updated in consequence,
+else the corresponding values are unmasked.
+        """
+        m = self._mask
+        # Hard mask: Get rid of the values/indices that fall on masked data
+        if self._hardmask and self._mask is not nomask:
+            mask = self._mask[indices]
+            indices = numeric.asarray(indices)
+            values = numeric.asanyarray(values)
+            values.resize(indices.shape)
+            indices = indices[~mask]
+            values = values[~mask]
+        #....
+        self._data.put(indices, values, mode=mode)
+        #....
+        if m is nomask:
+            m = getmask(values)
+        else:
+            m = m.copy()
+            if getmask(values) is nomask:
+                m.put(indices, False, mode=mode)
+            else:
+                m.put(indices, values._mask, mode=mode)
+            m = make_mask(m, copy=False, small_mask=True)
+        self._mask = m
+    #............................................
+    def ids (self):
+        """Return the address of the data and mask areas."""
+        return (self.ctypes.data, self._mask.ctypes.data)
+    #............................................
+    def all(self, axis=None, out=None):
+        """a.all(axis) returns True if all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as True during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+    Note: the out argument is not really operational...
+        """
+        d = self.filled(True).all(axis=axis, out=out).view(type(self))
+        if d.ndim > 0:
+            d.__setmask__(self._mask.all(axis))
+        return d
+
+    def any(self, axis=None, out=None):
+        """a.any(axis) returns True if some or all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as False during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+    Note: the out argument is not really operational...
+        """
+        d = self.filled(False).any(axis=axis, out=out).view(type(self))
+        if d.ndim > 0:
+            d.__setmask__(self._mask.all(axis))
+        return d
+
+    def nonzero(self):
+        """a.nonzero() returns a tuple of arrays
+
+    Returns a tuple of arrays, one for each dimension of a,
+    containing the indices of the non-zero elements in that
+    dimension.  The corresponding non-zero values can be obtained
+    with
+        a[a.nonzero()].
+
+    To group the indices by element, rather than dimension, use
+        transpose(a.nonzero())
+    instead. The result of this is always a 2d array, with a row for
+    each non-zero element."""
+        return numeric.asarray(self.filled(0)).nonzero()
+    #............................................
+    def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
+        """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
+Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
+        """
+        # TODO: What are we doing with `out`?
+        m = self._mask
+        if m is nomask:
+            result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
+                                                    axis2=axis2, out=out)
+            return result.astype(dtype)
+        else:
+            D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
+            return D.astype(dtype).sum(axis=None)
+    #............................................
+    def sum(self, axis=None, dtype=None):
+        """a.sum(axis=None, dtype=None)
+Sums the array `a` over the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+    """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
+        if result.ndim > 0:
+            result.__setmask__(mask)
+        return result
+
+    def cumsum(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative sum of the elements of array `a` along the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def prod(self, axis=None, dtype=None):
+        """a.prod(axis=None, dtype=None)
+Returns the product of the elements of array `a` along the given axis `axis`.
+Masked elements are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
+        if result.ndim:
+            result.__setmask__(mask)
+        return result
+    product = prod
+
+    def cumprod(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative product of ethe lements of array `a` along the given axis `axis`.
+Masked values are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def mean(self, axis=None, dtype=None):
+        """a.mean(axis=None, dtype=None)
+
+    Averages the array over the given axis.  If the axis is None,
+    averages over all dimensions of the array.  Equivalent to
+
+      a.sum(axis, dtype) / size(a, axis).
+
+    The optional dtype argument is the data type for intermediate
+    calculations in the sum.
+
+    Returns a masked array, of the same class as a.
+        """
+        if self._mask is nomask:
+            return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
+        else:
+            dsum = self.sum(axis=axis, dtype=dtype)
+            cnt = self.count(axis=axis)
+            return dsum*1./cnt
+
+    def anom(self, axis=None, dtype=None):
+        """a.anom(axis=None, dtype=None)
+    Returns the anomalies, or deviation from the average.
+            """
+        m = self.mean(axis, dtype)
+        if not axis:
+            return (self - m)
+        else:
+            return (self - expand_dims(m,axis))
+
+    def var(self, axis=None, dtype=None):
+        """a.var(axis=None, dtype=None)
+Returns the variance, a measure of the spread of a distribution.
+
+The variance is the average of the squared deviations from the mean,
+i.e. var = mean((x - x.mean())**2).
+        """
+        if self._mask is nomask:
+            # TODO: Do we keep super, or var _data and take a view ?
+            return super(MaskedArray, self).var(axis=axis, dtype=dtype)
+        else:
+            cnt = self.count(axis=axis)
+            danom = self.anom(axis=axis, dtype=dtype)
+            danom *= danom
+            dvar = danom.sum(axis) / cnt
+#            dvar /= cnt
+            if axis is not None:
+                dvar._mask = mask_or(self._mask.all(axis), (cnt==1))
+            return dvar
+
+    def std(self, axis=None, dtype=None):
+        """a.std(axis=None, dtype=None)
+Returns the standard deviation, a measure of the spread of a distribution.
+
+The standard deviation is the square root of the average of the squared
+deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
+        """
+        dvar = self.var(axis,dtype)
+        if axis is not None or dvar is not masked:
+            dvar = sqrt(dvar)
+        return dvar
+    #............................................
+    def argsort(self, axis=None, fill_value=None, kind='quicksort',
+                order=None):
+        """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self)
+        d = self.filled(fill_value)
+        return d.argsort(axis=axis, kind=kind, order=order)
+    #........................
+    def argmin(self, axis=None, fill_value=None):
+        """Returns a ndarray of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+        """
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        d = self.filled(fill_value)
+        return d.argmin(axis)
+    #........................
+    def argmax(self, axis=None, fill_value=None):
+        """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+        """
+        if fill_value is None:
+            fill_value = maximum_fill_value(self._data)
+        d = self.filled(fill_value)
+        return d.argmax(axis)
+
+    def sort(self, axis=-1, kind='quicksort', order=None,
+             endwith=True, fill_value=None):
+        """
+        Sort a along the given axis.
+
+    Keyword arguments:
+
+    axis  -- axis to be sorted (default -1)
+    kind  -- sorting algorithm (default 'quicksort')
+             Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+    order -- If a has fields defined, then the order keyword can be the
+             field name to sort on or a list (or tuple) of field names
+             to indicate the order that fields should be used to define
+             the sort.
+    endwith--Boolean flag indicating whether missing values (if any) should
+             be forced in the upper indices (at the end of the array) or
+             lower indices (at the beginning).
+
+    Returns: None.
+
+    This method sorts 'a' in place along the given axis using the algorithm
+    specified by the kind keyword.
+
+    The various sorts may characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order and is most
+    useful when used with argsort where the key might differ from the items
+    being sorted. The three available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is
+    not along the last axis. Consequently, sorts along the last axis are faster
+    and use less space than sorts along other axis.
+
+    """
+        if fill_value is None:
+            if endwith:
+                filler = minimum_fill_value(self)
+            else:
+                filler = maximum_fill_value(self)
+        else:
+            filler = fill_value
+        indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
+        self[:] = self[indx]
+        return
+    #............................................
+    def min(self, axis=None, fill_value=None):
+        """Returns the minimum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).min(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Get the mask ................
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fil value ...........
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).min(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def max(self, axis=None, fill_value=None):
+        """Returns the maximum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).max(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Check teh mask ..............
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fill value ..........
+        if fill_value is None:
+            fill_value = maximum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).max(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def ptp(self, axis=None, fill_value=None):
+        """Returns the visible data range (max-min) along the given axis.
+If the axis is `None`, applies on a flattened array. Masked values are filled
+with `fill_value` for processing. If `fill_value` is None, the maximum is uses
+the maximum default, the minimum uses the minimum default."""
+        return self.max(axis, fill_value) - self.min(axis, fill_value)
+
+    # Array methods ---------------------------------------
+    conj = conjugate = _arraymethod('conjugate')
+    copy = _arraymethod('copy')
+    diagonal = _arraymethod('diagonal')
+    take = _arraymethod('take')
+    ravel = _arraymethod('ravel')
+    transpose = _arraymethod('transpose')
+    T = property(fget=lambda self:self.transpose())
+    swapaxes = _arraymethod('swapaxes')
+    clip = _arraymethod('clip', onmask=False)
+    compress = _arraymethod('compress')
+    copy = _arraymethod('copy')
+    squeeze = _arraymethod('squeeze')
+    #--------------------------------------------
+    def tolist(self, fill_value=None):
+        """Copies the data portion of the array to a hierarchical python list and
+    returns that list. Data items are converted to the nearest compatible Python
+    type. Masked values are filled with `fill_value`"""
+        return self.filled(fill_value).tolist()
+    #........................
+    def tostring(self, fill_value=None):
+        """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string.
+
+    Keyword arguments:
+        order      : order of the data item in the copy {"C","F","A"} (default "C")
+        fill_value : value used in lieu of missing data
+
+    Construct a Python string containing the raw bytes in the array. The order
+    of the data in arrays with ndim > 1 is specified by the 'order' keyword and
+    this keyword overrides the order of the array. The
+    choices are:
+
+        "C"       -- C order (row major)
+        "Fortran" -- Fortran order (column major)
+        "Any"     -- Current order of array.
+        None      -- Same as "Any"
+
+    Masked data are filled with fill_value. If fill_value is None, the data-type-
+    dependent default is used."""
+        return self.filled(fill_value).tostring()
+    #--------------------------------------------
+    # Backwards Compatibility. Heck...
+    @property
+    def data(self):
+        """Returns the `_data` part of the MaskedArray.
+You should really use `_data` instead..."""
+        return self._data
+    def raw_data(self):
+        """Returns the `_data` part of the MaskedArray.
+You should really use `_data` instead..."""
+        return self._data
+
+##..............................................................................
+
+
+
+#class _arithmethods:
+#    """Defines a wrapper for arithmetic methods.
+#Instead of directly calling a ufunc, the corresponding method of  the `array._data`
+#object is called instead.
+#    """
+#    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+#        """
+#:Parameters:
+#    - `methodname` (String) : Method name.
+#    - `fill_self` (Float *[0]*) : Fill value for the instance.
+#    - `fill_other` (Float *[0]*) : Fill value for the target.
+#    - `domain` (Domain object *[None]*) : Domain of non-validity.
+#        """
+#        self.methodname = methodname
+#        self.fill_self = fill_self
+#        self.fill_other = fill_other
+#        self.domain = domain
+#    #
+#    def __call__ (self, instance, other, *args):
+#        "Execute the call behavior."
+#        m_self = instance._mask
+#        m_other = getmask(other)
+#        base = filled(instance,self.fill_self)
+#        target = filled(other, self.fill_other)
+#        if self.domain is not None:
+#            # We need to force the domain to a ndarray only.
+#            if self.fill_other > self.fill_self:
+#                domain = self.domain(base, target)
+#            else:
+#                domain = self.domain(target, base)
+#            if domain.any():
+#                #If `other` is a subclass of ndarray, `filled` must have the
+#                # same subclass, else we'll lose some info.
+#                #The easiest then is to fill `target` instead of creating
+#                # a pure ndarray.
+#                #Oh, and we better make a copy!
+#                if isinstance(other, ndarray):
+#                    if target is other:
+#                        # We don't want to modify other: let's copy target, then
+#                        target = target.copy()
+#                    target[:] = numeric.where(fromnumeric.asarray(domain),
+#                                              self.fill_other, target)
+#                else:
+#                    target = numeric.where(fromnumeric.asarray(domain),
+#                                           self.fill_other, target)
+#                m_other = mask_or(m_other, domain)
+#        m = mask_or(m_self, m_other)
+#        method = getattr(base, self.methodname)
+#        return instance.__class__(method(target, *args), mask=m)
+#    #
+#    def patch(self):
+#        """Applies the method `func` from class `method` to MaskedArray"""
+#        return types.MethodType(self,None,MaskedArray)
+#..............................................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Shortcuts ---
+#####---------------------------------------------------------------------------
+def isMaskedArray(x):
+    "Is x a masked array, that is, an instance of MaskedArray?"
+    return isinstance(x, MaskedArray)
+isarray = isMaskedArray
+isMA = isMaskedArray  #backward compatibility
+#masked = MaskedArray(0, int, mask=1)
+masked_singleton = MaskedArray(0, dtype=int_, mask=True)
+masked = masked_singleton
+
+masked_array = MaskedArray
+def array(data, dtype=None, copy=False, order=False, mask=nomask,
+          keep_mask=True, small_mask=True, hard_mask=None, fill_value=None):
+    """array(data, dtype=None, copy=True, order=False, mask=nomask,
+             keep_mask=True, small_mask=True, fill_value=None)
+Acts as shortcut to MaskedArray, with options in a different order for convenience.
+And backwards compatibility...
+    """
+    #TODO: we should try to put 'order' somwehere
+    return MaskedArray(data, mask=mask, dtype=dtype, copy=copy,
+                       keep_mask=keep_mask, small_mask=small_mask,
+                       hard_mask=hard_mask, fill_value=fill_value)
+
+def is_masked(x):
+    """Returns whether x has some masked values."""
+    m = getmask(x)
+    if m is nomask:
+        return False
+    elif m.any():
+        return True
+    return False
+
+
+#####--------------------------------------------------------------------------
+#---- --- Patch methods ---
+#####--------------------------------------------------------------------------
+#class _arraymethod:
+#    """Defines a wrapper for basic array methods.
+#Upon call, returns a masked array, where the new `_data` array is the output
+#of the corresponding method called on the original `_data`.
+#
+#If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+#If `onmask` is False, the new mask is just a reference to the initial mask.
+#
+#:Parameters:
+#    `funcname` : String
+#        Name of the function to apply on data.
+#    `onmask` : Boolean *[True]*
+#        Whether the mask must be processed also (True) or left alone (False).
+#    """
+#    def __init__(self, funcname, onmask=True):
+#        self._name = funcname
+#        self._onmask = onmask
+#        self.__doc__ = getattr(ndarray, self._name).__doc__
+#    def __call__(self, instance, *args, **params):
+#        methodname = self._name
+#        (d,m) = (instance._data, instance._mask)
+#        C = instance.__class__
+#        if m is nomask:
+#            return C(getattr(d,methodname).__call__(*args, **params))
+#        elif self._onmask:
+#            return C(getattr(d,methodname).__call__(*args, **params),
+#                     mask=getattr(m,methodname)(*args, **params) )
+#        else:
+#            return C(getattr(d,methodname).__call__(*args, **params), mask=m)
+#
+#    def patch(self):
+#        "Adds the new method to MaskedArray."
+#        return types.MethodType(self, None, MaskedArray)
+##......................................
+#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch()
+#MaskedArray.diagonal = _arraymethod('diagonal').patch()
+#MaskedArray.take = _arraymethod('take').patch()
+#MaskedArray.ravel = _arraymethod('ravel').patch()
+#MaskedArray.transpose = _arraymethod('transpose').patch()
+#MaskedArray.T = _arraymethod('transpose').patch()
+#MaskedArray.swapaxes = _arraymethod('swapaxes').patch()
+#MaskedArray.clip = _arraymethod('clip', onmask=False).patch()
+#MaskedArray.compress = _arraymethod('compress').patch()
+#MaskedArray.resize = _arraymethod('resize').patch()
+#MaskedArray.copy = _arraymethod('copy').patch()
+
+
+
+#####---------------------------------------------------------------------------
+#---- --- Extrema functions ---
+#####---------------------------------------------------------------------------
+class _extrema_operation(object):
+    def __call__(self, a, b=None):
+        "Executes the call behavior."
+        if b is None:
+            return self.reduce(a)
+        return where(self.compare(a, b), a, b)
+    #.........
+    def reduce(self, target, axis=None):
+        """Reduces target along the given axis."""
+        m = getmask(target)
+        if axis is not None:
+            kargs = { 'axis' : axis }
+        else:
+            kargs = {}
+
+        if m is nomask:
+            t = self.ufunc.reduce(target, **kargs)
+        else:
+            target = target.filled(self.fill_value_func(target)).view(type(target))
+            t = self.ufunc.reduce(target, **kargs)
+            m = umath.logical_and.reduce(m, **kargs)
+            if hasattr(t, '_mask'):
+                t._mask = m
+            elif m:
+                t = masked
+        return t
+    #.........
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = logical_or.outer(ma, mb)
+        result = self.ufunc.outer(filled(a), filled(b))
+        result._mask = m
+        return result
+#............................
+class _minimum_operation(_extrema_operation):
+    "Object to calculate minima"
+    def __init__ (self):
+        """minimum(a, b) or minimum(a)
+In one argument case, returns the scalar minimum.
+        """
+        self.ufunc = umath.minimum
+        self.afunc = amin
+        self.compare = less
+        self.fill_value_func = minimum_fill_value
+#............................
+class _maximum_operation(_extrema_operation):
+    "Object to calculate maxima"
+    def __init__ (self):
+        """maximum(a, b) or maximum(a)
+           In one argument case returns the scalar maximum.
+        """
+        self.ufunc = umath.maximum
+        self.afunc = amax
+        self.compare = greater
+        self.fill_value_func = maximum_fill_value
+#..........................................................
+def min(array, axis=None, out=None):
+    """Returns the minima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return minimum(array)
+    else:
+        return minimum.reduce(array, axis)
+#............................
+def max(obj, axis=None, out=None):
+    """Returns the maxima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return maximum(obj)
+    else:
+        return maximum.reduce(obj, axis)
+#.............................
+def ptp(obj, axis=None):
+    """a.ptp(axis=None) =  a.max(axis)-a.min(axis)"""
+    try:
+        return obj.max(axis)-obj.min(axis)
+    except AttributeError:
+        return max(obj, axis=axis) - min(obj, axis=axis)
+
+
+#####---------------------------------------------------------------------------
+#---- --- Definition of functions from the corresponding methods ---
+#####---------------------------------------------------------------------------
+class _frommethod:
+    """Defines functions from existing MaskedArray methods.
+:ivar _methodname (String): Name of the method to transform.
+    """
+    def __init__(self, methodname):
+        self._methodname = methodname
+        self.__doc__ = self.getdoc()
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self._methodname).__doc__
+        except:
+            return getattr(numpy, self._methodname).__doc__
+    def __call__(self, a, *args, **params):
+        if isinstance(a, MaskedArray):
+            return getattr(a, self._methodname).__call__(*args, **params)
+        #FIXME ----
+        #As x is not a MaskedArray, we transform it to a ndarray with asarray
+        #... and call the corresponding method.
+        #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
+        #we end up with a "SystemError: NULL result without error in PyObject_Call"
+        #A dirty trick is then to call the initial numpy function...
+        method = getattr(fromnumeric.asarray(a), self._methodname)
+        try:
+            return method(*args, **params)
+        except SystemError:
+            return getattr(numpy,self._methodname).__call__(a, *args, **params)
+
+all = _frommethod('all')
+anomalies = anom = _frommethod('anom')
+any = _frommethod('any')
+conjugate = _frommethod('conjugate')
+ids = _frommethod('ids')
+nonzero = _frommethod('nonzero')
+diagonal = _frommethod('diagonal')
+maximum = _maximum_operation()
+mean = _frommethod('mean')
+minimum = _minimum_operation ()
+product = _frommethod('prod')
+ptp = _frommethod('ptp')
+ravel = _frommethod('ravel')
+repeat = _frommethod('repeat')
+std = _frommethod('std')
+sum = _frommethod('sum')
+swapaxes = _frommethod('swapaxes')
+take = _frommethod('take')
+var = _frommethod('var')
+
+#..............................................................................
+def power(a, b, third=None):
+    """Computes a**b elementwise.
+    Masked values are set to 1."""
+    if third is not None:
+        raise MAError, "3-argument power not supported."
+    ma = getmask(a)
+    mb = getmask(b)
+    m = mask_or(ma, mb)
+    fa = filled(a, 1)
+    fb = filled(b, 1)
+    if fb.dtype.char in typecodes["Integer"]:
+        return masked_array(umath.power(fa, fb), m)
+    md = make_mask((fa < 0), small_mask=1)
+    m = mask_or(m, md)
+    if m is nomask:
+        return masked_array(umath.power(fa, fb))
+    else:
+        fa[m] = 1
+        return masked_array(umath.power(fa, fb), m)
+
+#..............................................................................
+def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None):
+    """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argsort(kind=kind, order=order)
+    return d.argsort(axis, kind=kind, order=order)
+
+def argmin(a, axis=None, fill_value=None):
+    """Returns the array of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    return d.argmin(axis=axis)
+
+def argmax(a, axis=None, fill_value=None):
+    """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+        try:
+            fill_value = - fill_value
+        except:
+            pass
+    d = filled(a, fill_value)
+    return d.argmax(axis=axis)
+
+def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+    """
+    Sort a along the given axis.
+
+Keyword arguments:
+
+axis  -- axis to be sorted (default -1)
+kind  -- sorting algorithm (default 'quicksort')
+         Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+order -- If a has fields defined, then the order keyword can be the
+         field name to sort on or a list (or tuple) of field names
+         to indicate the order that fields should be used to define
+         the sort.
+endwith--Boolean flag indicating whether missing values (if any) should
+         be forced in the upper indices (at the end of the array) or
+         lower indices (at the beginning).
+
+Returns: None.
+
+This method sorts 'a' in place along the given axis using the algorithm
+specified by the kind keyword.
+
+The various sorts may characterized by average speed, worst case
+performance, need for work space, and whether they are stable. A stable
+sort keeps items with the same key in the same relative order and is most
+useful when used with argsort where the key might differ from the items
+being sorted. The three available algorithms have the following properties:
+
+|------------------------------------------------------|
+|    kind   | speed |  worst case | work space | stable|
+|------------------------------------------------------|
+|'quicksort'|   1   | O(n^2)      |     0      |   no  |
+|'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+|'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+|------------------------------------------------------|
+
+All the sort algorithms make temporary copies of the data when the sort is
+not along the last axis. Consequently, sorts along the last axis are faster
+and use less space than sorts along other axis.
+
+"""
+    a = numeric.asanyarray(a)
+    if fill_value is None:
+        if endwith:
+            filler = minimum_fill_value(a)
+        else:
+            filler = maximum_fill_value(a)
+    else:
+        filler = fill_value
+#    return
+    indx = numpy.indices(a.shape).tolist()
+    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+    return a[indx]
+
+def compressed(x):
+    """Returns a compressed version of a masked array (or just the array if it
+    wasn't masked first)."""
+    if getmask(x) is None:
+        return x
+    else:
+        return x.compressed()
+
+def count(a, axis = None):
+    "Count of the non-masked elements in a, or along a certain axis."
+    a = masked_array(a)
+    return a.count(axis)
+
+def concatenate(arrays, axis=0):
+    "Concatenates the arrays along the given axis"
+    d = numeric.concatenate([filled(a) for a in arrays], axis)
+    rcls = get_masked_subclass(*arrays)
+    data = d.view(rcls)
+    for x in arrays:
+        if getmask(x) is not nomask:
+            break
+    else:
+        return data
+    dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis)
+    dm = make_mask(dm, copy=False, small_mask=True)
+    data._mask = dm
+    return data
+
+def expand_dims(x,axis):
+    """Expand the shape of a by including newaxis before given axis."""
+    result = n_expand_dims(x,axis)
+    if isinstance(x, MaskedArray):
+        new_shape = result.shape
+        result = x.view()
+        result.shape = new_shape
+        if result._mask is not nomask:
+            result._mask.shape = new_shape
+    return result
+
+#......................................
+def left_shift (a, n):
+    "Left shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.left_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.left_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+
+def right_shift (a, n):
+    "Right shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.right_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.right_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+#......................................
+def put(a, indices, values, mode='raise'):
+    """Sets storage-indexed locations to corresponding values.
+    Values and indices are filled if necessary."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.put(indices, values, mode=mode)
+    except AttributeError:
+        return fromnumeric.asarray(a).put(indices, values, mode=mode)
+
+def putmask(a, mask, values): #, mode='raise'):
+    """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true.
+If `v` is shorter than `mask`, it will be repeated as necessary.
+In particular `v` can be a scalar or length 1 array."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.putmask(values, mask)
+    except AttributeError:
+        return fromnumeric.asarray(a).putmask(values, mask)
+
+def transpose(a,axes=None):
+    """Returns a view of the array with dimensions permuted according to axes.
+If `axes` is None (default), returns array with dimensions reversed.
+    """
+    #We can't use 'frommethod', as 'transpose' doesn't take keywords
+    try:
+        return a.transpose(axes)
+    except AttributeError:
+        return fromnumeric.asarray(a).transpose(axes)
+
+def reshape(a, new_shape):
+    """Changes the shape of the array `a` to `new_shape`."""
+    #We can't use 'frommethod', it whine about some parameters. Dmmit.
+    try:
+        return a.reshape(new_shape)
+    except AttributeError:
+        return fromnumeric.asarray(a).reshape(new_shape)
+
+def resize(x, new_shape):
+    """resize(a,new_shape) returns a new array with the specified shape.
+    The total size of the original array can be any size.
+    The new array is filled with repeated copies of a. If a was masked, the new
+    array will be masked, and the new mask will be a repetition of the old one.
+    """
+    # We can't use _frommethods here, as N.resize is notoriously whiny.
+    m = getmask(x)
+    if m is not nomask:
+        m = fromnumeric.resize(m, new_shape)
+    result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x))
+    if result.ndim:
+        result._mask = m
+    return result
+
+
+#................................................
+def rank(obj):
+    """Gets the rank of sequence a (the number of dimensions, not a matrix rank)
+The rank of a scalar is zero."""
+    return fromnumeric.rank(filled(obj))
+#
+def shape(obj):
+    """Returns the shape of `a` (as a function call which also works on nested sequences).
+    """
+    return fromnumeric.shape(filled(obj))
+#
+def size(obj, axis=None):
+    """Returns the number of elements in the array along the given axis,
+or in the sequence if `axis` is None.
+    """
+    return fromnumeric.size(filled(obj), axis)
+#................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Extra functions ---
+#####--------------------------------------------------------------------------
+def where (condition, x, y):
+    """where(condition, x, y) is x where condition is nonzero, y otherwise.
+       condition must be convertible to an integer array.
+       Answer is always the shape of condition.
+       The type depends on x and y. It is integer if both x and y are
+       the value masked.
+    """
+    fc = filled(not_equal(condition, 0), 0)
+    xv = filled(x)
+    xm = getmask(x)
+    yv = filled(y)
+    ym = getmask(y)
+    d = numeric.choose(fc, (yv, xv))
+    md = numeric.choose(fc, (ym, xm))
+    m = getmask(condition)
+    m = make_mask(mask_or(m, md), copy=False, small_mask=True)
+    return masked_array(d, mask=m)
+
+def choose (indices, t, out=None, mode='raise'):
+    "Returns array shaped like indices with elements chosen from t"
+    #TODO: implement options `out` and `mode`, if possible.
+    def fmask (x):
+        "Returns the filled array, or True if ``masked``."
+        if x is masked:
+            return 1
+        return filled(x)
+    def nmask (x):
+        "Returns the mask, True if ``masked``, False if ``nomask``."
+        if x is masked:
+            return 1
+        m = getmask(x)
+        if m is nomask:
+            return 0
+        return m
+    c = filled(indices, 0)
+    masks = [nmask(x) for x in t]
+    a = [fmask(x) for x in t]
+    d = numeric.choose(c, a)
+    m = numeric.choose(c, masks)
+    m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1)
+    return masked_array(d, mask=m)
+
+def round_(a, decimals=0, out=None):
+    """Returns reference to result. Copies a and rounds to 'decimals' places.
+
+    Keyword arguments:
+        decimals -- number of decimals to round to (default 0). May be negative.
+        out -- existing array to use for output (default copy of a).
+
+    Return:
+        Reference to out, where None specifies a copy of the original array a.
+
+    Round to the specified number of decimals. When 'decimals' is negative it
+    specifies the number of positions to the left of the decimal point. The
+    real and imaginary parts of complex numbers are rounded separately.
+    Nothing is done if the array is not of float type and 'decimals' is greater
+    than or equal to 0."""
+    result = fromnumeric.round_(filled(a), decimals, out)
+    if isinstance(a,MaskedArray):
+        result = result.view(type(a))
+        result._mask = a._mask
+    else:
+        result = result.view(MaskedArray)
+    return result
+
+def arange(start, stop=None, step=1, dtype=None):
+    """Just like range() except it returns a array whose type can be specified
+    by the keyword argument dtype.
+    """
+    return array(numeric.arange(start, stop, step, dtype),mask=nomask)
+
+def inner(a, b):
+    """inner(a,b) returns the dot product of two arrays, which has
+    shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the
+    product of the elements from the last dimensions of a and b.
+    Masked elements are replace by zeros.
+    """
+    fa = filled(a, 0)
+    fb = filled(b, 0)
+    if len(fa.shape) == 0:
+        fa.shape = (1,)
+    if len(fb.shape) == 0:
+        fb.shape = (1,)
+    return masked_array(numeric.inner(fa, fb))
+innerproduct = inner
+
+def outer(a, b):
+    """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))"""
+    fa = filled(a, 0).ravel()
+    fb = filled(b, 0).ravel()
+    d = numeric.outer(fa, fb)
+    ma = getmask(a)
+    mb = getmask(b)
+    if ma is nomask and mb is nomask:
+        return masked_array(d)
+    ma = getmaskarray(a)
+    mb = getmaskarray(b)
+    m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
+    return masked_array(d, mask=m)
+outerproduct = outer
+
+def allequal (a, b, fill_value=True):
+    """
+Returns `True` if all entries of  a and b are equal, using
+fill_value as a truth value where either or both are masked.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    if m is nomask:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        return d.all()
+    elif fill_value:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        dm = array(d, mask=m, copy=False)
+        return dm.filled(True).all(None)
+    else:
+        return False
+
+def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+    """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances.
+If `fill_value` is True, masked values are considered equal.
+If `fill_value` is False, masked values considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error `atol` comes into play for those elements of `b`
+ that are very small or zero; it says how small `a` must be also.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    d1 = filled(a)
+    d2 = filled(b)
+    x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
+    y = filled(array(d2, copy=0, mask=m), 1).astype(float)
+    d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+    return fromnumeric.alltrue(fromnumeric.ravel(d))
+
+#..............................................................................
+def asarray(a, dtype=None):
+    """asarray(data, dtype) = array(data, dtype, copy=0)
+Returns `a` as an masked array.
+No copy is performed if `a` is already an array.
+Subclasses are converted to base class MaskedArray.
+    """
+    return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
+
+def empty(new_shape, dtype=float):
+    """empty((d1,...,dn),dtype=float,order='C')
+Returns a new array of shape (d1,...,dn) and given type with all its
+entries uninitialized. This can be faster than zeros."""
+    return numeric.empty(new_shape, dtype).view(MaskedArray)
+
+def empty_like(a):
+    """empty_like(a)
+Returns an empty (uninitialized) array of the shape and typecode of a.
+Note that this does NOT initialize the returned array.
+If you require your array to be initialized, you should use zeros_like()."""
+    return numeric.empty_like(a).view(MaskedArray)
+
+def ones(new_shape, dtype=float):
+    """ones(shape, dtype=None)
+Returns an array of the given dimensions, initialized to all ones."""
+    return numeric.ones(new_shape, dtype).view(MaskedArray)
+
+def zeros(new_shape, dtype=float):
+    """zeros(new_shape, dtype=None)
+Returns an array of the given dimensions, initialized to all zeros."""
+    return numeric.zeros(new_shape, dtype).view(MaskedArray)
+
+#####--------------------------------------------------------------------------
+#---- --- Pickling ---
+#####--------------------------------------------------------------------------
+#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data
+def _mareconstruct(subtype, baseshape, basetype,):
+    """Internal function that builds a new MaskedArray from the information stored
+in a pickle."""
+    _data = ndarray.__new__(ndarray, baseshape, basetype)
+    _mask = ndarray.__new__(ndarray, baseshape, basetype)
+    return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False)
+
+def _getstate(a):
+    "Returns the internal state of the masked array, for pickling purposes."
+    state = (1,
+             a.shape,
+             a.dtype,
+             a.flags.fnc,
+             a.tostring(),
+             getmaskarray(a).tostring())
+    return state
+
+def _setstate(a, state):
+    """Restores the internal state of the masked array, for pickling purposes.
+`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
+
+    - class name
+    - a tuple giving the shape of the data
+    - a typecode for the data
+    - a binary string for the data
+    - a binary string for the mask.
+        """
+    (ver, shp, typ, isf, raw, msk) = state
+    super(MaskedArray, a).__setstate__((shp, typ, isf, raw))
+    (a._mask).__setstate__((shp, dtype('|b1'), isf, msk))
+
+def _reduce(a):
+    """Returns a 3-tuple for pickling a MaskedArray."""
+    return (_mareconstruct,
+            (a.__class__, (0,), 'b', ),
+            a.__getstate__())
+
+def dump(a,F):
+    """Pickles the MaskedArray `a` to the file `F`.
+`F` can either be the handle of an exiting file, or a string representing a file name.
+    """
+    if not hasattr(F,'readline'):
+        F = open(F,'w')
+    return cPickle.dump(a,F)
+
+def dumps(a):
+    """Returns a string corresponding to the pickling of the MaskedArray."""
+    return cPickle.dumps(a)
+
+def load(F):
+    """Wrapper around ``cPickle.load`` which accepts either a file-like object or
+ a filename."""
+    if not hasattr(F, 'readline'):
+        F = open(F,'r')
+    return cPickle.load(F)
+
+def loads(strg):
+    "Loads a pickle from the current string."""
+    return cPickle.loads(strg)
+
+MaskedArray.__getstate__ = _getstate
+MaskedArray.__setstate__ = _setstate
+MaskedArray.__reduce__ = _reduce
+MaskedArray.__dump__ = dump
+MaskedArray.__dumps__ = dumps
+
+################################################################################
+
+if __name__ == '__main__':
+    import numpy as N
+    from maskedarray.testutils import assert_equal, assert_array_equal, assert_mask_equal
+    pi = N.pi
+#    coremodule = __main__
+
+    a = array([1,2,3,4,5],mask=[0,1,0,0,0])
+    x = add.reduce(a)
+    assert_equal(add.reduce(a), 13)
+
+    if 0:
+        x = masked_array([1,2,3], mask=[1,0,0])
+        mx = masked_array(x)
+        assert_equal(mx.mask, x.mask)
+        mx = masked_array(x, mask=[0,1,0], keep_mask=False)
+        assert_equal(mx.mask, [0,1,0])
+        mx = masked_array(x, mask=[0,1,0], keep_mask=True)
+        assert_equal(mx.mask, [1,1,0])
+        #We default to true
+        mx = masked_array(x, mask=[0,1,0])
+        assert_equal(mx.mask, [1,1,0])
+    if 0:
+        import numpy.core.ma as nma
+        x = nma.arange(5)
+        x[2] = nma.masked
+        X = masked_array(x, mask=x._mask)
+        assert_equal(X._mask, x.mask)
+        assert_equal(X._data, x._data)
+        X = masked_array(x)
+        assert_equal(X._data, x._data)
+        assert_equal(X._mask, x.mask)
+        assert_equal(getmask(x), [0,0,1,0,0])
+    if 0:
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        x = array(d, mask = m)
+        assert( x[3] is masked)
+        assert( x[4] is masked)
+        x[[1,4]] = [10,40]
+        assert( x.mask is not m)
+        assert( x[3] is masked)
+        assert( x[4] is not masked)
+        assert_equal(x, [0,10,2,-1,40])
+    if 0:
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        xh = array(d, mask = m, hard_mask=True)
+        # We need to copy, to avoid updating d in xh!
+        xs = array(d, mask = m, hard_mask=False, copy=True)
+        xh[[1,4]] = [10,40]
+        xs[[1,4]] = [10,40]
+        assert_equal(xh._data, [0,10,2,3,4])
+        assert_equal(xs._data, [0,10,2,3,40])
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        assert_equal(xs.mask, [0,0,0,1,0])
+        assert(xh._hardmask)
+        assert(not xs._hardmask)
+        xh[1:4] = [10,20,30]
+        xs[1:4] = [10,20,30]
+        assert_equal(xh._data, [0,10,20,3,4])
+        assert_equal(xs._data, [0,10,20,30,40])
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        assert_equal(xs.mask, nomask)
+        xh[0] = masked
+        xs[0] = masked
+        assert_equal(xh.mask, [1,0,0,1,1])
+        assert_equal(xs.mask, [1,0,0,0,0])
+        xh[:] = 1
+        xs[:] = 1
+        assert_equal(xh._data, [0,1,1,3,4])
+        assert_equal(xs._data, [1,1,1,1,1])
+        assert_equal(xh.mask, [1,0,0,1,1])
+        assert_equal(xs.mask, nomask)
+        # Switch to soft mask
+        xh.soften_mask()
+        xh[:] = arange(5)
+        assert_equal(xh._data, [0,1,2,3,4])
+        assert_equal(xh.mask, nomask)
+        # Switch back to hard mask
+        xh.harden_mask()
+        xh[xh<3] = masked
+        assert_equal(xh._data, [0,1,2,3,4])
+        assert_equal(xh._mask, [1,1,1,0,0])
+        xh[filled(xh>1,False)] = 5
+        assert_equal(xh._data, [0,1,2,5,5])
+        assert_equal(xh._mask, [1,1,1,0,0])
+        #
+        xh = array([[1,2],[3,4]], mask = [[1,0],[0,0]], hard_mask=True)
+        xh[0] = 0
+        assert_equal(xh._data, [[1,0],[3,4]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        xh[-1,-1] = 5
+        assert_equal(xh._data, [[1,0],[3,5]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        xh[filled(xh<5,False)] = 2
+        assert_equal(xh._data, [[1,2],[2,5]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        #
+        "Another test of hardmask"
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        xh = array(d, mask = m, hard_mask=True)
+        xh[4:5] = 999
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        xh[0:1] = 999
+        assert_equal(xh._data,[999,1,2,3,4])
+    if 0:
+        x = N.array([[ 0.13,  0.26,  0.90],
+                     [ 0.28,  0.33,  0.63],
+                     [ 0.31,  0.87,  0.70]])
+        m = N.array([[ True, False, False],
+                     [False, False, False],
+                     [True,  True, False]], dtype=N.bool_)
+        mx = masked_array(x, mask=m)
+        xbig = N.array([[False, False,  True],
+                        [False, False,  True],
+                        [False,  True,  True]], dtype=N.bool_)
+        mxbig = (mx > 0.5)
+        mxsmall = (mx < 0.5)
+        #
+        assert (mxbig.all()==False)
+        assert (mxbig.any()==True)
+        y = mxbig.all(0)
+        assert_equal(mxbig.all(0),[False, False, True])
+        assert_equal(mxbig.all(1), [False, False, True])
+        assert_equal(mxbig.any(0),[False, False, True])
+        assert_equal(mxbig.any(1), [True, True, True])
+
+    if 1:
+        "Tests put."
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        x = array(d, mask = m)
+        assert( x[3] is masked)
+        assert( x[4] is masked)
+        x[[1,4]] = [10,40]
+        assert( x.mask is not m)
+        assert( x[3] is masked)
+        assert( x[4] is not masked)
+        assert_equal(x, [0,10,2,-1,40])
+        #
+        x = masked_array(arange(10), mask=[1,0,0,0,0]*2)
+        i = [0,2,4,6]
+        x.put(i, [6,4,2,0])
+        assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,]))
+        assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0])
+        x.put(i, masked_array([0,2,4,6],[1,0,1,0]))
+        assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,])
+        assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0])
+        #
+        x = masked_array(arange(10), mask=[1,0,0,0,0]*2)
+        put(x, i, [6,4,2,0])
+        assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,]))
+        assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0])
+        put(x, i, masked_array([0,2,4,6],[1,0,1,0]))
+        assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,])
+        assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0])
+
+    if 1:
+        x = arange(20)
+        x = x.reshape(4,5)
+        x.flat[5] = 12
+        assert x[1,0] == 12
+        z = x + 10j * x
+        assert_equal(z.real, x)
+        assert_equal(z.imag, 10*x)
+        assert_equal((z*conjugate(z)).real, 101*x*x)
+        z.imag[...] = 0.0
+    if 1:
+        x = array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6)
+        y = array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6)
+        d = (x,y)
+
+        z = N.add(x, y)
+
+        for f in ['sqrt', 'log', 'log10', 'exp', 'conjugate',
+                  'sin', 'cos', 'tan',
+                  'arcsin', 'arccos', 'arctan',
+                  'sinh', 'cosh', 'tanh',
+                  'arcsinh',
+                  'arccosh',
+                  'arctanh',
+                  'absolute', 'fabs', 'negative',
+                  # 'nonzero', 'around',
+                  'floor', 'ceil',
+                  # 'sometrue', 'alltrue',
+                  'logical_not',
+                  'add', 'subtract', 'multiply',
+                  'divide', 'true_divide', 'floor_divide',
+                  'remainder', 'fmod', 'hypot', 'arctan2',
+                  'equal', 'not_equal', 'less_equal', 'greater_equal',
+                  'less', 'greater',
+                  'logical_and', 'logical_or', 'logical_xor',
+                  ]:
+            print f
+            try:
+                uf = getattr(umath, f)
+            except AttributeError:
+                uf = getattr(fromnumeric, f)
+            mf = eval(f) #getattr(coremodule, f)
+            args = d[:uf.nin]
+            ur = uf(*args)
+            mr = mf(*args)
+            assert_equal(ur.filled(0), mr.filled(0), f)
+            assert_mask_equal(ur.mask, mr.mask)

Added: trunk/scipy/sandbox/maskedarray/alternative_versions/core_ini.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/alternative_versions/core_ini.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/alternative_versions/core_ini.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,3033 @@
+"""MA: a facility for dealing with missing observations
+MA is generally used as a numpy.array look-alike.
+by Paul F. Dubois.
+
+Copyright 1999, 2000, 2001 Regents of the University of California.
+Released for unlimited redistribution.
+Adapted for numpy_core 2005 by Travis Oliphant and
+(mainly) Paul Dubois.
+
+Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
+pgmdevlist_at_gmail_dot_com
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: core_ini.py 3473 2007-10-29 15:18:13Z jarrod.millman $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3473 $"
+__date__     = '$Date: 2007-10-29 08:18:13 -0700 (Mon, 29 Oct 2007) $'
+
+__all__ = ['MAError', 'MaskType', 'MaskedArray',
+           'bool_', 'complex_', 'float_', 'int_', 'object_',
+           'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
+               'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
+               'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
+               'arctanh', 'argmax', 'argmin', 'argsort', 'around',
+               'array', 'asarray',
+           'bitwise_and', 'bitwise_or', 'bitwise_xor',
+           'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
+               'cos', 'cosh', 'count',
+           'diagonal', 'divide', 'dump', 'dumps',
+           'empty', 'empty_like', 'equal', 'exp',
+           'fabs', 'fmod', 'filled', 'floor', 'floor_divide',
+           'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
+           'ids', 'inner', 'innerproduct',
+               'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
+           'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
+               'logical_and', 'logical_not', 'logical_or', 'logical_xor',
+           'make_mask', 'make_mask_none', 'mask_or', 'masked',
+               'masked_array', 'masked_equal', 'masked_greater',
+               'masked_greater_equal', 'masked_inside', 'masked_less',
+               'masked_less_equal', 'masked_not_equal', 'masked_object',
+               'masked_outside', 'masked_print_option', 'masked_singleton',
+               'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
+               'minimum', 'multiply',
+           'negative', 'nomask', 'nonzero', 'not_equal',
+           'ones', 'outer', 'outerproduct',
+           'power', 'product', 'ptp', 'put', 'putmask',
+           'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
+               'right_shift', 'round_',
+           'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
+               'subtract', 'sum', 'swapaxes',
+           'take', 'tan', 'tanh', 'transpose', 'true_divide',
+           'var', 'where',
+           'zeros']
+
+import sys
+import types
+import cPickle
+#
+import numpy
+from numpy import bool_, complex_, float_, int_, object_, str_
+
+import numpy.core.umath as umath
+import numpy.core.fromnumeric  as fromnumeric
+from numpy.core.numeric import ndarray
+from numpy.core.fromnumeric import amax, amin
+import numpy.core.numerictypes as ntypes
+from numpy.core.numerictypes import bool_, typecodes
+from numpy.core.multiarray import dtype
+import numpy.core.numeric as numeric
+from numpy.lib.shape_base import expand_dims as n_expand_dims
+import warnings
+
+
+
+MaskType = bool_
+nomask = MaskType(0)
+
+divide_tolerance = 1.e-35
+numpy.seterr(all='ignore')
+
+#####--------------------------------------------------------------------------
+#---- --- Helper functions ---
+#####--------------------------------------------------------------------------
+def convert_typecode(f,dtchar):
+    """Converts the type of `f` to a type compatible with `dtchar`, for inline operations."""
+    ftype = f.dtype.char
+    if dtchar == ftype:
+        return f
+    elif dtchar in typecodes['Integer']:
+        if ftype in typecodes['Integer']:
+            f = f.astype(dtchar)
+        else:
+            raise TypeError, 'Incorrect type for in-place operation.'
+    elif dtchar in typecodes['Float']:
+        if ftype in typecodes['Integer']:
+            f = f.astype(dtchar)
+        elif ftype in typecodes['Float']:
+            f = f.astype(dtchar)
+        else:
+            raise TypeError, 'Incorrect type for in-place operation.'
+    elif dtchar in typecodes['Complex']:
+        if ftype in typecodes['Integer']:
+            f = f.astype(dtchar)
+        elif ftype in typecodes['Float']:
+            f = f.astype(dtchar)
+        elif ftype in typecodes['Complex']:
+            f = f.astype(dtchar)
+        else:
+            raise TypeError, 'Incorrect type for in-place operation.'
+    else:
+        raise TypeError, 'Incorrect type for in-place operation.'
+    return f
+
+#####--------------------------------------------------------------------------
+#---- --- Exceptions ---
+#####--------------------------------------------------------------------------
+class MAError(Exception):
+    "Class for MA related errors."
+    def __init__ (self, args=None):
+        "Creates an exception."
+        Exception.__init__(self,args)
+        self.args = args
+    def __str__(self):
+        "Calculates the string representation."
+        return str(self.args)
+    __repr__ = __str__
+
+#####--------------------------------------------------------------------------
+#---- --- Filling options ---
+#####--------------------------------------------------------------------------
+# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
+default_filler = {'b': True,
+                  'c' : 1.e20 + 0.0j,
+                  'f' : 1.e20,
+                  'i' : 999999,
+                  'O' : '?',
+                  'S' : 'N/A',
+                  'u' : 999999,
+                  'V' : '???',
+                  }
+#{0: <type 'numpy.bool_'>,
+# 1: <type 'numpy.int8'>,
+# 2: <type 'numpy.uint8'>,
+# 3: <type 'numpy.int16'>,
+# 4: <type 'numpy.uint16'>,
+# 5: <type 'numpy.int32'>,
+# 6: <type 'numpy.uint32'>,
+# 7: <type 'numpy.int64'>,
+# 8: <type 'numpy.uint64'>,
+# 9: <type 'numpy.int64'>,
+# 10: <type 'numpy.uint64'>,
+# 11: <type 'numpy.float32'>,
+# 12: <type 'numpy.float64'>,
+# 13: <type 'numpy.float128'>,
+# 14: <type 'numpy.complex64'>,
+# 15: <type 'numpy.complex128'>,
+# 16: <type 'numpy.complex256'>,
+# 17: <type 'numpy.object_'>,
+# 18: <type 'numpy.string_'>,
+# 19: <type 'numpy.unicode_'>,
+# 20: <type 'numpy.void'>,
+max_filler = ntypes._minvals
+max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]])
+min_filler = ntypes._maxvals
+min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]])
+if 'float128' in ntypes.typeDict:
+    max_filler.update([(numpy.float128,-numeric.inf)])
+    min_filler.update([(numpy.float128, numeric.inf)])
+
+
+def default_fill_value (obj):
+    "Calculates the default fill value for an object `obj`."
+    if hasattr(obj,'dtype'):
+        return default_filler[obj.dtype.kind]
+    elif isinstance(obj, float):
+        return default_filler['f']
+    elif isinstance(obj, int) or isinstance(obj, long):
+        return default_filler['i']
+    elif isinstance(obj, str):
+        return default_filler['S']
+    elif isinstance(obj, complex):
+        return default_filler['c']
+    elif isinstance(obj, numeric.dtype):
+        return default_filler[obj.kind]
+    else:
+        return default_filler['O']
+
+def minimum_fill_value (obj):
+    "Calculates the default fill value suitable for taking the minimum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = min_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return min_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return min_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return min_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return min_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def maximum_fill_value (obj):
+    "Calculates the default fill value suitable for taking the maximum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = max_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return max_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return max_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return max_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return max_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def set_fill_value (a, fill_value):
+    "Sets the fill value of `a` if it is a masked array."
+    if isinstance(a, MaskedArray):
+        a.set_fill_value(fill_value)
+
+def get_fill_value (a):
+    """Returns the fill value of `a`, if any.
+    Otherwise, returns the default fill value for that type.
+    """
+    if isinstance(a, MaskedArray):
+        result = a.fill_value
+    else:
+        result = default_fill_value(a)
+    return result
+
+def common_fill_value (a, b):
+    "Returns the common fill_value of `a` and `b`, if any, or `None`."
+    t1 = get_fill_value(a)
+    t2 = get_fill_value(b)
+    if t1 == t2:
+        return t1
+    return None
+
+#................................................
+def filled(a, value = None):
+    """Returns `a` as an array with masked data replaced by `value`.
+If `value` is `None` or the special element `masked`, `get_fill_value(a)`
+is used instead.
+
+If `a` is already a contiguous numeric array, `a` itself is returned.
+
+`filled(a)` can be used to be sure that the result is numeric when passing
+an object a to other software ignorant of MA, in particular to numpy itself.
+    """
+    if hasattr(a, 'filled'):
+        return a.filled(value)
+    elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
+        return a
+    elif isinstance(a, types.DictType):
+        return numeric.array(a, 'O')
+    else:
+        return numeric.array(a)
+
+#####--------------------------------------------------------------------------
+#---- --- Ufuncs ---
+#####--------------------------------------------------------------------------
+ufunc_domain = {}
+ufunc_fills = {}
+
+class domain_check_interval:
+    """Defines a valid interval,
+so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`."""
+    def __init__(self, a, b):
+        "domain_check_interval(a,b)(x) = true where x < a or y > b"
+        if (a > b):
+            (a, b) = (b, a)
+        self.a = a
+        self.b = b
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.logical_or(umath.greater (x, self.b),
+                                umath.less(x, self.a))
+#............................
+class domain_tan:
+    """Defines a valid interval for the `tan` function,
+so that `domain_tan(eps) = True where `abs(cos(x)) < eps`"""
+    def __init__(self, eps):
+        "domain_tan(eps) = true where abs(cos(x)) < eps)"
+        self.eps = eps
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(umath.absolute(umath.cos(x)), self.eps)
+#............................
+class domain_safe_divide:
+    """defines a domain for safe division."""
+    def __init__ (self, tolerance=divide_tolerance):
+        self.tolerance = tolerance
+    def __call__ (self, a, b):
+        return umath.absolute(a) * self.tolerance >= umath.absolute(b)
+#............................
+class domain_greater:
+    "domain_greater(v)(x) = true where x <= v"
+    def __init__(self, critical_value):
+        "domain_greater(v)(x) = true where x <= v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less_equal(x, self.critical_value)
+#............................
+class domain_greater_equal:
+    "domain_greater_equal(v)(x) = true where x < v"
+    def __init__(self, critical_value):
+        "domain_greater_equal(v)(x) = true where x < v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(x, self.critical_value)
+#..............................................................................
+class masked_unary_operation:
+    """Defines masked version of unary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fill` : Default filling value *[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mufunc, fill=0, domain=None):
+        """ masked_unary_operation(aufunc, fill=0, domain=None)
+            aufunc(fill) must be defined
+            self(x) returns aufunc(x)
+            with masked values where domain(x) is true or getmask(x) is true.
+        """
+        self.f = mufunc
+        self.fill = fill
+        self.domain = domain
+        self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
+        self.__name__ = getattr(mufunc, "__name__", str(mufunc))
+        ufunc_domain[mufunc] = domain
+        ufunc_fills[mufunc] = fill
+    #
+    def __call__ (self, a, *args, **kwargs):
+        "Execute the call behavior."
+# numeric tries to return scalars rather than arrays when given scalars.
+        m = getmask(a)
+        d1 = filled(a, self.fill)
+        if self.domain is not None:
+            m = mask_or(m, numeric.asarray(self.domain(d1)))
+        result = self.f(d1, *args, **kwargs)
+        #
+        if isinstance(result, MaskedArray):
+            return result.__class__(result, mask=m)
+        return masked_array(result, mask=m)
+    #
+    def __str__ (self):
+        return "Masked version of %s. [Invalid values are masked]" % str(self.f)
+#..............................................................................
+class masked_binary_operation:
+    """Defines masked version of binary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mbfunc, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = mbfunc
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
+        self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
+        ufunc_domain[mbfunc] = None
+        ufunc_fills[mbfunc] = (fillx, filly)
+    #
+    def __call__ (self, a, b, *args, **kwargs):
+        "Execute the call behavior."
+        m = mask_or(getmask(a), getmask(b))
+        d1 = filled(a, self.fillx)
+        d2 = filled(b, self.filly)
+        result = self.f(d1, d2, *args, **kwargs)
+#        if isinstance(result, ndarray) \
+#               and m.ndim != 0 \
+#               and m.shape != result.shape:
+#            m = mask_or(getmaskarray(a), getmaskarray(b))
+        if isinstance(result, MaskedArray):
+            return result.__class__(result, mask=m)
+        return masked_array(result, mask=m)
+    #
+    def reduce (self, target, axis=0, dtype=None):
+        """Reduces `target` along the given `axis`."""
+        if isinstance(target, MaskedArray):
+            tclass = target.__class__
+        else:
+            tclass = MaskedArray
+        m = getmask(target)
+        t = filled(target, self.filly)
+        if t.shape == ():
+            t = t.reshape(1)
+            if m is not nomask:
+                m = make_mask(m, copy=1)
+                m.shape = (1,)
+        if m is nomask:
+            return tclass(self.f.reduce (t, axis))
+        else:
+            t = tclass(t, mask=m)
+            # XXX: "or t.dtype" below is a workaround for what appears
+            # XXX: to be a bug in reduce.
+            t = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype)
+            m = umath.logical_and.reduce(m, axis)
+            if isinstance(t, ndarray):
+                return tclass(t, mask=m, fill_value=get_fill_value(target))
+            elif m:
+                return masked
+            else:
+                return t
+
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = umath.logical_or.outer(ma, mb)
+        d = self.f.outer(filled(a, self.fillx), filled(b, self.filly))
+        if isinstance(d, MaskedArray):
+            return d.__class__(d, mask=m)
+        return masked_array(d, mask=m)
+
+    def accumulate (self, target, axis=0):
+        """Accumulates `target` along `axis` after filling with y fill value."""
+        if isinstance(target, MaskedArray):
+            tclass = target.__class__
+        else:
+            tclass = masked_array
+        t = filled(target, self.filly)
+        return tclass(self.f.accumulate(t, axis))
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+#..............................................................................
+class domained_binary_operation:
+    """Defines binary operations that have a domain, like divide.
+
+These are complicated so they are a separate class.
+They have no reduce, outer or accumulate.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, dbfunc, domain, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = dbfunc
+        self.domain = domain
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
+        self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
+        ufunc_domain[dbfunc] = domain
+        ufunc_fills[dbfunc] = (fillx, filly)
+
+    def __call__(self, a, b):
+        "Execute the call behavior."
+        ma = getmask(a)
+        mb = getmask(b)
+        d1 = filled(a, self.fillx)
+        d2 = filled(b, self.filly)
+        t = numeric.asarray(self.domain(d1, d2))
+        if fromnumeric.sometrue(t, None):
+            d2 = numeric.where(t, self.filly, d2)
+            mb = mask_or(mb, t)
+        m = mask_or(ma, mb)
+        result =  self.f(d1, d2)
+        if isinstance(result, MaskedArray):
+            return result.__class__(result, mask=m)
+        return masked_array(result, mask=m)
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+
+#..............................................................................
+# Unary ufuncs
+exp = masked_unary_operation(umath.exp)
+conjugate = masked_unary_operation(umath.conjugate)
+sin = masked_unary_operation(umath.sin)
+cos = masked_unary_operation(umath.cos)
+tan = masked_unary_operation(umath.tan)
+arctan = masked_unary_operation(umath.arctan)
+arcsinh = masked_unary_operation(umath.arcsinh)
+sinh = masked_unary_operation(umath.sinh)
+cosh = masked_unary_operation(umath.cosh)
+tanh = masked_unary_operation(umath.tanh)
+abs = absolute = masked_unary_operation(umath.absolute)
+fabs = masked_unary_operation(umath.fabs)
+negative = masked_unary_operation(umath.negative)
+floor = masked_unary_operation(umath.floor)
+ceil = masked_unary_operation(umath.ceil)
+around = masked_unary_operation(fromnumeric.round_)
+logical_not = masked_unary_operation(umath.logical_not)
+# Domained unary ufuncs
+sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0))
+log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0))
+log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0))
+tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35))
+arcsin = masked_unary_operation(umath.arcsin, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccos = masked_unary_operation(umath.arccos, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0))
+arctanh = masked_unary_operation(umath.arctanh, 0.0,
+                                 domain_check_interval(-1.0+1e-15, 1.0-1e-15))
+# Binary ufuncs
+add = masked_binary_operation(umath.add)
+subtract = masked_binary_operation(umath.subtract)
+multiply = masked_binary_operation(umath.multiply, 1, 1)
+arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0)
+equal = masked_binary_operation(umath.equal)
+equal.reduce = None
+not_equal = masked_binary_operation(umath.not_equal)
+not_equal.reduce = None
+less_equal = masked_binary_operation(umath.less_equal)
+less_equal.reduce = None
+greater_equal = masked_binary_operation(umath.greater_equal)
+greater_equal.reduce = None
+less = masked_binary_operation(umath.less)
+less.reduce = None
+greater = masked_binary_operation(umath.greater)
+greater.reduce = None
+logical_and = masked_binary_operation(umath.logical_and)
+alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce
+logical_or = masked_binary_operation(umath.logical_or)
+sometrue = logical_or.reduce
+logical_xor = masked_binary_operation(umath.logical_xor)
+bitwise_and = masked_binary_operation(umath.bitwise_and)
+bitwise_or = masked_binary_operation(umath.bitwise_or)
+bitwise_xor = masked_binary_operation(umath.bitwise_xor)
+hypot = masked_binary_operation(umath.hypot)
+# Domained binary ufuncs
+divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1)
+true_divide = domained_binary_operation(umath.true_divide,
+                                        domain_safe_divide(), 0, 1)
+floor_divide = domained_binary_operation(umath.floor_divide,
+                                         domain_safe_divide(), 0, 1)
+remainder = domained_binary_operation(umath.remainder,
+                                      domain_safe_divide(), 0, 1)
+fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Mask creation functions ---
+#####--------------------------------------------------------------------------
+def getmask(a):
+    """Returns the mask of `a`, if any, or `nomask`.
+Returns `nomask` if `a` is not a masked array.
+To get an array for sure use getmaskarray."""
+    if hasattr(a, "_mask"):
+        return a._mask
+    else:
+        return nomask
+
+def getmaskarray(a):
+    """Returns the mask of `a`, if any.
+Otherwise, returns an array of `False`, with the same shape as `a`.
+    """
+    m = getmask(a)
+    if m is nomask:
+        return make_mask_none(fromnumeric.shape(a))
+    else:
+        return m
+
+def is_mask(m):
+    """Returns `True` if `m` is a legal mask.
+Does not check contents, only type.
+    """
+    try:
+        return m.dtype.type is MaskType
+    except AttributeError:
+        return False
+#
+def make_mask(m, copy=False, small_mask=True, flag=None):
+    """make_mask(m, copy=0, small_mask=0)
+Returns `m` as a mask, creating a copy if necessary or requested.
+The function can accept any sequence of integers or `nomask`.
+Does not check that contents must be 0s and 1s.
+If `small_mask=True`, returns `nomask` if `m` contains no true elements.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+    """
+    if flag is not None:
+        warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                      DeprecationWarning)
+        small_mask = flag
+    if m is nomask:
+        return nomask
+    elif isinstance(m, ndarray):
+        m = filled(m, True)
+        if m.dtype.type is MaskType:
+            if copy:
+                result = numeric.array(m, dtype=MaskType, copy=copy)
+            else:
+                result = m
+        else:
+            result = numeric.array(m, dtype=MaskType)
+    else:
+        result = numeric.array(filled(m, True), dtype=MaskType)
+    # Bas les masques !
+    if small_mask and not result.any():
+        return nomask
+    else:
+        return result
+
+def make_mask_none(s):
+    "Returns a mask of shape `s`, filled with `False`."
+    result = numeric.zeros(s, dtype=MaskType)
+    return result
+
+def mask_or (m1, m2, copy=False, small_mask=True):
+    """Returns the combination of two masks `m1` and `m2`.
+The masks are combined with the `logical_or` operator, treating `nomask` as false.
+The result may equal m1 or m2 if the other is nomask.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+     """
+    if m1 is nomask:
+        return make_mask(m2, copy=copy, small_mask=small_mask)
+    if m2 is nomask:
+        return make_mask(m1, copy=copy, small_mask=small_mask)
+    if m1 is m2 and is_mask(m1):
+        return m1
+    return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
+
+#####--------------------------------------------------------------------------
+#--- --- Masking functions ---
+#####--------------------------------------------------------------------------
+def masked_where(condition, x, copy=True):
+    """Returns `x` as an array masked where `condition` is true.
+Masked values of `x` or `condition` are kept.
+
+:Parameters:
+    - `condition` (ndarray) : Masking condition.
+    - `x` (ndarray) : Array to mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    """
+    cm = filled(condition,1)
+    if isinstance(x,MaskedArray):
+        m = mask_or(x._mask, cm)
+        return x.__class__(x._data, mask=m, copy=copy)
+    else:
+        return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm)
+
+def masked_greater(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x > value)``."
+    return masked_where(greater(x, value), x, copy=copy)
+
+def masked_greater_equal(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x >= value)``."
+    return masked_where(greater_equal(x, value), x, copy=copy)
+
+def masked_less(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x < value)``."
+    return masked_where(less(x, value), x, copy=copy)
+
+def masked_less_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x <= value)``."
+    return masked_where(less_equal(x, value), x, copy=copy)
+
+def masked_not_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x != value)``."
+    return masked_where((x != value), x, copy=copy)
+
+#
+def masked_equal(x, value, copy=True):
+    """Shortcut to `masked_where`, with ``condition = (x == value)``.
+For floating point, consider `masked_values(x, value)` instead.
+    """
+    return masked_where((x == value), x, copy=copy)
+#    d = filled(x, 0)
+#    c = umath.equal(d, value)
+#    m = mask_or(c, getmask(x))
+#    return array(d, mask=m, copy=copy)
+
+def masked_inside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x inside
+the interval `[v1,v2]` ``(v1 <= x <= v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf >= v1) & (xf <= v2)
+    return masked_where(condition, x, copy=copy)
+
+def masked_outside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x outside
+the interval `[v1,v2]` ``(x < v1)|(x > v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf < v1) | (xf > v2)
+    return masked_where(condition, x, copy=copy)
+
+#
+def masked_object(x, value, copy=True):
+    """Masks the array `x` where the data are exactly equal to `value`.
+This function is suitable only for `object` arrays: for floating point,
+please use `masked_values` instead.
+The mask is set to `nomask` if posible.
+
+:parameter copy (Boolean, *[True]*):  Returns a copy of `x` if true. """
+    if isMaskedArray(x):
+        condition = umath.equal(x._data, value)
+        mask = x._mask
+    else:
+        condition = umath.equal(fromnumeric.asarray(x), value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(x, mask=mask, copy=copy, fill_value=value)
+
+def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
+    """Masks the array `x` where the data are approximately equal to `value`
+(that is, ``abs(x - value) <= atol+rtol*abs(value)``).
+Suitable only for floating points. For integers, please use `masked_equal`.
+The mask is set to `nomask` if posible.
+
+:Parameters:
+    - `rtol` (Float, *[1e-5]*): Tolerance parameter.
+    - `atol` (Float, *[1e-8]*): Tolerance parameter.
+    - `copy` (boolean, *[False]*) : Returns a copy of `x` if True.
+    """
+    abs = umath.absolute
+    xnew = filled(x, value)
+    if issubclass(xnew.dtype.type, numeric.floating):
+        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+        try:
+            mask = x._mask
+        except AttributeError:
+            mask = nomask
+    else:
+        condition = umath.equal(xnew, value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
+
+#####--------------------------------------------------------------------------
+#---- --- Printing options ---
+#####--------------------------------------------------------------------------
+class _MaskedPrintOption:
+    """Handles the string used to represent missing data in a masked array."""
+    def __init__ (self, display):
+        "Creates the masked_print_option object."
+        self._display = display
+        self._enabled = True
+
+    def display(self):
+        "Displays the string to print for masked values."
+        return self._display
+
+    def set_display (self, s):
+        "Sets the string to print for masked values."
+        self._display = s
+
+    def enabled(self):
+        "Is the use of the display value enabled?"
+        return self._enabled
+
+    def enable(self, small_mask=1):
+        "Set the enabling small_mask to `small_mask`."
+        self._enabled = small_mask
+
+    def __str__ (self):
+        return str(self._display)
+
+    __repr__ = __str__
+
+#if you single index into a masked location you get this object.
+masked_print_option = _MaskedPrintOption('--')
+
+#####--------------------------------------------------------------------------
+#---- --- MaskedArray class ---
+#####--------------------------------------------------------------------------
+class MaskedArray(numeric.ndarray):
+    """Arrays with possibly masked values.
+Masked values of True exclude the corresponding element from any computation.
+
+Construction:
+    x = array(data, dtype=None, copy=True, order=False,
+              mask = nomask, fill_value=None, small_mask=True)
+
+If copy=False, every effort is made not to copy the data:
+If `data` is a MaskedArray, and argument mask=nomask, then the candidate data
+is `data._data` and the mask used is `data._mask`.
+If `data` is a numeric array, it is used as the candidate raw data.
+If `dtype` is not None and is different from data.dtype.char then a data copy is required.
+Otherwise, the candidate is used.
+
+If a data copy is required, the raw (unmasked) data stored is the result of:
+numeric.array(data, dtype=dtype.char, copy=copy)
+
+If `mask` is `nomask` there are no masked values.
+Otherwise mask must be convertible to an array of booleans with the same shape as x.
+If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`.
+Otherwise, the mask is not compressed.
+
+fill_value is used to fill in masked values when necessary, such as when
+printing and in method/function filled().
+The fill_value is not used for computation within this module.
+    """
+    __array_priority__ = 10.1
+    _defaultmask = nomask
+    _defaulthardmask = False
+    #TODO: There some reorganization to do round here
+    def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
+                keep_mask=True, small_mask=True, hard_mask=False, flag=None,
+                **options):
+        """array(data, dtype=None, copy=True, mask=nomask, fill_value=None)
+
+If `data` is already a ndarray, its dtype becomes the default value of dtype.
+        """
+        if flag is not None:
+            warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                          DeprecationWarning)
+            small_mask = flag
+        # 1. Argument is MA ...........
+        if isinstance(data, MaskedArray) or\
+           (hasattr(data,"_mask") and hasattr(data,"_data")) :
+            if data is masked:
+                return masked.view(cls)
+            if keep_mask:
+                if mask is nomask:
+                    if copy:
+                        cls._defaultmask = data._mask.copy()
+                    else:
+                        cls._defaultmask = data._mask
+                else:
+                    cls._defaultmask = mask_or(data._mask, mask,
+                                                copy=copy, small_mask=small_mask)
+            else:
+                cls._defaultmask = make_mask(mask, copy=copy, small_mask=small_mask)
+            # Update fille_value
+            if fill_value is None:
+                cls._fill_value = data._fill_value
+            else:
+                cls._fill_value = fill_value
+            cls._defaulthardmask = hard_mask
+            _data = data._data
+            if dtype is not None and _data.dtype != numeric.dtype(dtype):
+                return _data.astype(dtype).view(cls)
+            elif copy:
+                return _data.copy().view(cls)
+            else:
+                return _data.view(cls)
+        # 2. Argument is not MA .......
+        if isinstance(data, ndarray):
+            if dtype is not None and data.dtype != numeric.dtype(dtype):
+                _data = data.astype(dtype)
+            elif copy:
+                _data = data.copy()
+            else:
+                _data = data
+        else:
+            _data = numeric.array(data, dtype=dtype, copy=copy)
+#            try:
+#                _data = numeric.array(data, dtype=dtype, copy=copy)
+#            except TypeError:
+#                _data = empty(len(data), dtype=dtype)
+#                for (k,v) in enumerate(data):
+#                    _data[k] = v
+#                if mask is nomask:
+#                    cls.__defaultmask = getmask(_data)
+#                    return _data.view(cls)
+        # Define mask .................
+        mask = make_mask(mask, copy=copy, small_mask=small_mask)
+        #....Check shapes compatibility
+        if mask is not nomask:
+            (nd, nm) = (_data.size, mask.size)
+            if (nm != nd):
+                # We need to resize w/ a function, in case _data is only a reference
+                if nm == 1:
+                    mask = fromnumeric.resize(mask, _data.shape)
+                elif nd == 1:
+                    _data = fromnumeric.resize(_data, mask.shape)
+                else:
+                    msg = "Mask and data not compatible: data size is %i, "+\
+                          "mask size is %i."
+                    raise MAError, msg % (nd, nm)
+            elif (mask.shape != _data.shape):
+                mask = mask.reshape(_data.shape)
+#                mask = _data.shape
+        #....
+        cls._fill_value = fill_value
+        cls._defaulthardmask = hard_mask
+        cls._defaultmask = mask
+        cls._defaultoptions = options
+        return numeric.asanyarray(_data).view(cls)
+    #..................................
+    def __array_wrap__(self, obj, context=None):
+        """Special hook for ufuncs.
+Wraps the numpy array and sets the mask according to context.
+        """
+#        mclass = self.__class__
+        #..........
+        if context is None:
+#            return mclass(obj, mask=self._mask, copy=False)
+            return MaskedArray(obj, mask=self._mask, copy=False,
+                               dtype=obj.dtype,
+                               fill_value=self.fill_value, )
+        #..........
+        (func, args) = context[:2]
+        m = reduce(mask_or, [getmask(arg) for arg in args])
+        # Get domain mask
+        domain = ufunc_domain.get(func, None)
+        if domain is not None:
+            m = mask_or(m, domain(*[getattr(arg, '_data', arg) for arg in args]))
+        # Update mask
+        if m is not nomask:
+            try:
+                dshape = obj.shape
+            except AttributeError:
+                pass
+            else:
+                if m.shape != dshape:
+                    m = reduce(mask_or, [getmaskarray(arg) for arg in args])
+#        return mclass(obj, copy=False, mask=m)
+        return MaskedArray(obj, copy=False, mask=m,)
+#                           dtype=obj.dtype, fill_value=self._fill_value)
+    #........................
+    #TODO: there should be some reorganization to do round here.
+    def __array_finalize__(self,obj):
+        """Finalizes the masked array.
+        """
+        #
+        if isinstance(obj, MaskedArray):
+            # We came here from a MaskedArray
+            self._data = obj._data
+            self._mask = obj._mask
+            self._hardmask = obj._hardmask
+            self._fill_value = obj._fill_value
+            self.options = obj.options
+        else:
+            # We came here from a .view()
+            if hasattr(obj,'_data') and hasattr(obj, '_mask'):
+                # obj is an old masked array or a smart record
+                self._data = obj._data
+                self._mask = obj._mask
+            else:
+                # obj is anything but...
+                self._data = obj
+                self._mask = self._defaultmask
+            # Set the instance default
+            self._hardmask = self._defaulthardmask
+            self.fill_value = self._fill_value
+            self.options = self._defaultoptions
+            # Reset the class default
+            MaskedArray._defaultmask = nomask
+            MaskedArray._defaulthardmask = False
+            MaskedArray._fill_value = None
+#        #
+        return
+    #............................................
+    def __getitem__(self, indx):
+        """x.__getitem__(y) <==> x[y]
+Returns the item described by i. Not a copy as in previous versions.
+        """
+        if getmask(indx) is not nomask:
+            msg = "Masked arrays must be filled before they can be used as indices!"
+            raise IndexError, msg
+        dout = self._data[indx]
+        m = self._mask
+        scalardout = (len(numeric.shape(dout))==0)
+        #
+        if m is nomask:
+            if scalardout:
+                return dout
+            else:
+                return self.__class__(dout, mask=nomask, keep_mask=True,
+                                      fill_value=self._fill_value,
+                                      **self.options)
+        #....
+        mi = m[indx]
+        if mi.size == 1:
+            if mi:
+                return masked
+            return dout
+        else:
+            return self.__class__(dout, mask=mi, fill_value=self._fill_value,
+                                  **self.options)
+    #........................
+    def __setitem__(self, index, value):
+        """x.__setitem__(i, y) <==> x[i]=y
+Sets item described by index. If value is masked, masks those locations.
+        """
+        if self is masked:
+            raise MAError, 'Cannot alter the masked element.'
+        if getmask(index) is not nomask:
+            msg = "Masked arrays must be filled before they can be used as indices!"
+            raise IndexError, msg
+        #....
+        (d, m) = (self._data, self._mask)
+        #....
+        if value is masked:
+            if m is nomask:
+                m = make_mask_none(d.shape)
+            else:
+                m = m.copy()
+            m[index] = True
+            self._mask = m
+            return
+        #....
+        if m is nomask:
+            d[index] = filled(value)
+            valmask = getmask(value)
+            if valmask is not nomask:
+                m = make_mask_none(d.shape)
+                m[index] = valmask
+        elif not self._hardmask:
+            d[index] = filled(value)
+            valmask = getmask(value)
+            m = m.copy()
+            if valmask is nomask:
+                m[index] = False
+            else:
+                m[index] = valmask
+        elif hasattr(index, 'dtype') and (index.dtype==bool_):
+            index *= ~m
+            d[index] = filled(value)
+#        elif isinstance(index, int):
+        else:
+            mindx = m[index]
+            value = masked_array(value, mask=mindx, keep_mask=True)
+            valdata = filled(value)
+            valmask = getmask(value)
+            if valmask is nomask:
+                d[index] = valdata
+            elif valmask.size > 1:
+                dindx = d[index]
+                numeric.putmask(dindx, ~valmask, valdata)
+                d[index] = dindx
+                numeric.putmask(mindx, valmask, True)
+                m[index] = mindx
+        #.....
+        if not m.any():
+            self._mask = nomask
+        else:
+            self._mask = m
+    #............................................
+    def __getslice__(self, i, j):
+        """x.__getslice__(i, j) <==> x[i:j]
+Returns the slice described by i, j.
+The use of negative indices is not supported."""
+        m = self._mask
+        dout = self._data[i:j]
+        if m is nomask:
+            return self.__class__(dout, fill_value=self._fill_value,
+                                  **self.options)
+        else:
+            return self.__class__(dout, mask=m[i:j], fill_value=self._fill_value,
+                                  **self.options)
+    #........................
+    def __setslice__(self, i, j, value):
+        """x.__setslice__(i, j, value) <==> x[i:j]=value
+Sets a slice i:j to `value`.
+If `value` is masked, masks those locations."""
+        if self is masked:
+            #TODO: Well, maybe we could/should
+            raise MAError, "Cannot alter the 'masked' object."
+        #....
+        (d, m) = (self._data, self._mask)
+        #....
+        if value is masked:
+            if m is nomask:
+                m = make_mask_none(d.shape)
+            m[i:j] = True
+            self._mask = m
+            return
+        #....
+        if m is nomask:
+            valmask = getmask(value)
+            valdata = filled(value)
+            d[i:j] = valdata
+            if valmask is not nomask:
+                m = make_mask_none(d.shape)
+                m[i:j] = valmask
+        elif not self._hardmask:
+            valmask = getmask(value)
+            valdata = filled(value)
+            d[i:j] = valdata
+            if valmask is nomask:
+                m[i:j] = False
+            else:
+                m[i:j] = valmask
+        else:
+            mindx = m[i:j]
+            value = masked_array(value, mask=mindx, keep_mask=True)
+            valmask = value._mask
+            if valmask is nomask:
+                d[i:j][~mindx] = filled(value)
+            elif valmask.size > 1:
+                d[i:j][~mindx] = value[~valmask]
+                m[i:j][valmask] = True
+        #.....
+        if not m.any():
+            self._mask = nomask
+        else:
+            self._mask = m
+    #............................................
+    # If we don't want to crash the performance, we better leave __getattribute__ alone...
+#    def __getattribute__(self, name):
+#        """x.__getattribute__('name') = x.name
+#Returns the chosen attribute.
+#If the attribute cannot be directly accessed, checks the _data section.
+#        """
+#        try:
+#            return ndarray.__getattribute__(self, name)
+#        except AttributeError:
+#            pass
+#        try:
+#            return self._data.__getattribute__(name)
+#        except AttributeError:
+#            raise AttributeError
+    #............................................
+    def __str__(self):
+        """x.__str__() <==> str(x)
+Calculates the string representation, using masked for fill if it is enabled.
+Otherwise, fills with fill value.
+        """
+        if masked_print_option.enabled():
+            f = masked_print_option
+            if self is masked:
+                return str(f)
+            m = self._mask
+            if m is nomask:
+                res = self._data
+            else:
+                if m.shape == () and m:
+                    return str(f)
+                # convert to object array to make filled work
+#CHECK: the two lines below seem more robust than the self._data.astype
+#                res = numeric.empty(self._data.shape, object_)
+#                numeric.putmask(res,~m,self._data)
+                res = self._data.astype("|O8")
+                res[self._mask] = f
+        else:
+            res = self.filled(self.fill_value)
+        return str(res)
+
+    def __repr__(self):
+        """x.__repr__() <==> repr(x)
+Calculates the repr representation, using masked for fill if it is enabled.
+Otherwise fill with fill value.
+        """
+        with_mask = """\
+masked_%(name)s(data =
+ %(data)s,
+      mask =
+ %(mask)s,
+      fill_value=%(fill)s)
+"""
+        with_mask1 = """\
+masked_%(name)s(data = %(data)s,
+      mask = %(mask)s,
+      fill_value=%(fill)s)
+"""
+        n = len(self.shape)
+        name = repr(self._data).split('(')[0]
+        if n <= 1:
+            return with_mask1 % {
+                'name': name,
+                'data': str(self),
+                'mask': str(self._mask),
+                'fill': str(self.fill_value),
+                }
+        return with_mask % {
+            'name': name,
+            'data': str(self),
+            'mask': str(self._mask),
+            'fill': str(self.fill_value),
+            }
+    #............................................
+    def __abs__(self):
+        """x.__abs__() <==> abs(x)
+Returns a masked array of the current subclass, with the new `_data`
+the absolute of the inital `_data`.
+        """
+        return self.__class__(self._data.__abs__(), mask=self._mask,
+                              fill_value = self._fill_value, **self.options)
+    #
+    def __neg__(self):
+        """x.__abs__() <==> neg(x)
+Returns a masked array of the current subclass, with the new `_data`
+the negative of the inital `_data`."""
+        try:
+            return self.__class__(self._data.__neg__(), mask=self._mask,
+                                  fill_value = self._fill_value, **self.options)
+        except MAError:
+            return negative(self)
+    #
+    def __iadd__(self, other):
+        "Adds other to self in place."
+        f = convert_typecode(filled(other, 0), self._data.dtype.char)
+        m = getmask(other)
+        self._data += f
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #
+    def __isub__(self, other):
+        "Subtracts other from self in place."
+        f = convert_typecode(filled(other, 0), self._data.dtype.char)
+        m = getmask(other)
+        self._data -= f
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #
+    def __imul__(self, other):
+        "Multiplies self by other in place."
+        f = convert_typecode(filled(other, 1), self._data.dtype.char)
+        m = getmask(other)
+        self._data *= f
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #
+    def __idiv__(self, other):
+        "Divides self by other in place."
+        f = convert_typecode(filled(other, 0), self._data.dtype.char)
+        mo = getmask(other)
+        result = divide(self, masked_array(f, mask=mo))
+        self._data = result._data
+        dm = result._mask
+        if dm is not self._mask:
+            self._mask = dm
+        return self
+
+#    #
+#    def __eq__(self, other):
+#        return equal(self,other)
+#
+#    def __ne__(self, other):
+#        return not_equal(self,other)
+#
+#    def __lt__(self, other):
+#        return less(self,other)
+#
+#    def __le__(self, other):
+#        return less_equal(self,other)
+#
+#    def __gt__(self, other):
+#        return greater(self,other)
+#
+#    def __ge__(self, other):
+#        return greater_equal(self,other)
+
+    #............................................
+    def __float__(self):
+        "Converts self to float."
+        if self._mask is not nomask:
+            warnings.warn("Warning: converting a masked element to nan.")
+            return numpy.nan
+            #raise MAError, 'Cannot convert masked element to a Python float.'
+        return float(self._data.item())
+
+    def __int__(self):
+        "Converts self to int."
+        if self._mask is not nomask:
+            raise MAError, 'Cannot convert masked element to a Python int.'
+        return int(self._data.item())
+
+    @property
+    def dtype(self):
+        """returns the data type of `_data`."""
+        return self._data.dtype
+
+    def astype (self, tc):
+        """Returns self as an array of given type.
+Subclassing is preserved."""
+        if tc == self._data.dtype:
+            return self
+        try:
+            return self.__class__(self, mask=self._mask, dtype=tc, copy=True,
+                                  **self.options)
+        except:
+#            d = self._data.astype(tc)
+            return self.__class__(self._data.astype(tc), mask=self._mask,
+                                  dtype=tc, **self.options)
+#
+#
+    #............................................
+    def harden_mask(self):
+        "Forces the mask to hard"
+        self._hardmask = True
+    def soften_mask(self):
+        "Forces the mask to soft"
+        self._hardmask = False
+    #............................................
+    #TODO: FIX THAT: THAT"S NOT A REAL FLATITER
+    def _get_flat(self):
+        """Calculates the flat value.
+        """
+        if self._mask is nomask:
+            return masked_array(self._data.ravel(), mask=nomask, copy=False,
+                                fill_value = self._fill_value,
+                                **self.options)
+        else:
+            return masked_array(self._data.ravel(), mask=self._mask.ravel(),
+                                copy=False, fill_value = self._fill_value,
+                                **self.options)
+    #
+    def _set_flat (self, value):
+        "x.flat = value"
+        y = self.ravel()
+        y[:] = value
+    #
+    flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version")
+    #
+    #............................................
+    def _get_real(self):
+        "Returns the real part of a complex array."
+        return self.__class__(self._data.real, mask=self.mask,
+                              fill_value = self._fill_value, **self.options)
+    def _set_real (self, value):
+        "Sets the real part of a complex array to `value`."
+        y = self.real
+        y[...] = value
+
+    real = property(fget=_get_real, fset=_set_real, doc="Get it real!")
+
+    def _get_imaginary(self):
+        "Returns the imaginary part of a complex array."
+        return self.__class__(self._data.imag, mask=self.mask,
+                              fill_value = self._fill_value, **self.options)
+
+    def _set_imaginary (self, value):
+        "Sets the imaginary part of a complex array to `value`."
+        y = self.imaginary
+        y[...] = value
+
+    imag = property(fget=_get_imaginary, fset=_set_imaginary,
+                    doc="Imaginary part.")
+    imaginary = imag
+    #............................................
+    def _get_mask(self):
+        """Returns the current mask."""
+        return self._mask
+    def _set_mask(self, mask):
+        """Sets the mask to `mask`."""
+        mask = make_mask(mask, copy=False, small_mask=True)
+        if mask is not nomask:
+            if mask.size != self._data.size:
+                raise ValueError, "Inconsistent shape between data and mask!"
+            if mask.shape != self._data.shape:
+                mask.shape = self._data.shape
+            self._mask = mask
+        else:
+            self._mask = nomask
+    mask = property(fget=_get_mask, fset=_set_mask, doc="Mask")
+    #............................................
+    def get_fill_value(self):
+        "Returns the filling value."
+        return self._fill_value
+
+    def set_fill_value(self, value=None):
+        """Sets the filling value to `value`.
+If None, uses the default, based on the data type."""
+        if value is None:
+            value = default_fill_value(self._data)
+        self._fill_value = value
+
+    fill_value = property(fget=get_fill_value, fset=set_fill_value,
+                          doc="Filling value")
+
+    def filled(self, fill_value=None):
+        """Returns an array of the same class as `_data`,
+ with masked values filled with `fill_value`.
+Subclassing is preserved.
+
+If `fill_value` is None, uses self.fill_value.
+        """
+        d = self._data
+        m = self._mask
+        if m is nomask:
+            return d
+        #
+        if fill_value is None:
+            value = self.fill_value
+        else:
+            value = fill_value
+        #
+        if self is masked_singleton:
+            result = numeric.asanyarray(value)
+        else:
+            result = d.copy()
+            try:
+                result[m] = value
+            except (TypeError, AttributeError):
+                value = numeric.array(value, dtype=object)
+                d = d.astype(object)
+                result = fromnumeric.choose(m, (d, value))
+            except IndexError:
+                #ok, if scalar
+                if d.shape:
+                    raise
+                elif m:
+                    result = numeric.array(value, dtype=d.dtype)
+                else:
+                    result = d
+        return result
+
+    def compressed(self):
+        "A 1-D array of all the non-masked data."
+        d = self._data.ravel()
+        if self._mask is nomask:
+            return d
+        else:
+            return d[~self._mask.ravel()]
+    #............................................
+    def count(self, axis=None):
+        """Counts the non-masked elements of the array along a given axis,
+and returns a masked array where the mask is True where all data are masked.
+If `axis` is None, counts all the non-masked elements, and returns either a
+scalar or the masked singleton."""
+        m = self._mask
+        s = self._data.shape
+        ls = len(s)
+        if m is nomask:
+            if ls == 0:
+                return 1
+            if ls == 1:
+                return s[0]
+            if axis is None:
+                return self._data.size
+            else:
+                n = s[axis]
+                t = list(s)
+                del t[axis]
+                return numeric.ones(t) * n
+        n1 = fromnumeric.size(m, axis)
+        n2 = m.astype(int_).sum(axis)
+        if axis is None:
+            return (n1-n2)
+        else:
+            return masked_array(n1 - n2)
+    #............................................
+    def _get_shape(self):
+        "Returns the current shape."
+        return self._data.shape
+    #
+    def _set_shape (self, newshape):
+        "Sets the array's shape."
+        self._data.shape = newshape
+        if self._mask is not nomask:
+            #self._mask = self._mask.copy()
+            self._mask.shape = newshape
+    #
+    shape = property(fget=_get_shape, fset=_set_shape,
+                     doc="Shape of the array, as a tuple.")
+    #
+    def _get_size(self):
+        "Returns the current size."
+        return self._data.size
+    size = property(fget=_get_size,
+                    doc="Size (number of elements) of the array.")
+    #
+    def _get_ndim(self):
+        "Returns the number of dimensions."
+        return self._data.ndim
+    ndim = property(fget=_get_ndim,
+                    doc="Number of dimensions of the array.")
+    #
+    def reshape (self, *s):
+        """Reshapes the array to shape s.
+Returns a new masked array.
+If you want to modify the shape in place, please use `a.shape = s`"""
+        if self._mask is not nomask:
+            return self.__class__(self._data.reshape(*s),
+                                  mask=self._mask.reshape(*s),
+                                  fill_value=self.fill_value, **self.options)
+        else:
+            return self.__class__(self._data.reshape(*s),
+                                  fill_value=self.fill_value, **self.options)
+    #
+    def repeat(self, repeats, axis=None):
+        """Repeat elements of `a` `repeats` times along `axis`.
+`repeats` is a sequence of length `a.shape[axis]` telling how many times
+each element should be repeated.
+The mask is repeated accordingly.
+        """
+        f = self.filled()
+        if isinstance(repeats, types.IntType):
+            if axis is None:
+                num = f.size
+            else:
+                num = f.shape[axis]
+            repeats = tuple([repeats]*num)
+
+        m = self._mask
+        if m is not nomask:
+            m = fromnumeric.repeat(m, repeats, axis)
+        d = fromnumeric.repeat(f, repeats, axis)
+        return self.__class__(d, mask=m, fill_value=self.fill_value,
+                              **self.options)
+    #
+    def resize(self, newshape, refcheck=True, order=False):
+        """Attempts to modify size and shape of self inplace.
+        The array must own its own memory and not be referenced by other arrays.
+        Returns None.
+        """
+        try:
+            self._data.resize(newshape,)
+            if self.mask is not nomask:
+                self._mask.resize(newshape,)
+        except ValueError:
+            msg = "Cannot resize an array that has been referenced or "+\
+                  "is referencing another array in this way.\n"+\
+                  "Use the resize function."
+            raise ValueError, msg
+        return None
+    #
+    def flatten(self):
+        """Flattens the array in place.
+        """
+        flatsize = self.size
+        self._data.resize((flatsize,))
+        if self.mask is not nomask:
+            self._mask.resize((flatsize,))
+        return self
+
+    #
+    def put(self, indices, values, mode='raise'):
+        """Sets storage-indexed locations to corresponding values.
+a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
+`values` can be scalar or an array shorter than indices, and it will be repeat,
+if necessary.
+If `values` has some masked values, the initial mask is updated in consequence,
+else the corresponding values are unmasked.
+        """
+        #TODO: Check that
+        (d, m) = (self._data, self._mask)
+        ind = filled(indices)
+        v = filled(values)
+        d.put(ind, v, mode=mode)
+        if m is not nomask:
+            if getmask(values) is not nomask:
+                m.put(ind, values._mask, mode=mode)
+            else:
+                m.put(ind, False, mode=mode)
+            self._mask = make_mask(m, copy=False, small_mask=True)
+    #............................................
+    def ids (self):
+        """Return the ids of the data and mask areas."""
+        return (id(self._data), id(self._mask))
+    #............................................
+    def all(self, axis=None):
+        """a.all(axis) returns True if all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as True during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+        """
+        d = filled(self, True).all(axis)
+        m = self._mask.all(axis)
+        return self.__class__(d, mask=m, dtype=bool_,
+                              fill_value=self._fill_value, **self.options)
+    def any(self, axis=None):
+        """a.any(axis) returns True if some or all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as False during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+        """
+        d = filled(self, False).any(axis)
+        m = self._mask.all(axis)
+        return self.__class__(d, mask=m, dtype=bool_,
+                              fill_value=self._fill_value, **self.options)
+    def nonzero(self):
+        """a.nonzero() returns a tuple of arrays
+
+    Returns a tuple of arrays, one for each dimension of a,
+    containing the indices of the non-zero elements in that
+    dimension.  The corresponding non-zero values can be obtained
+    with
+        a[a.nonzero()].
+
+    To group the indices by element, rather than dimension, use
+        transpose(a.nonzero())
+    instead. The result of this is always a 2d array, with a row for
+    each non-zero element."""
+        return numeric.asarray(self.filled(0)).nonzero()
+    #............................................
+    def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
+        """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
+Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
+        """
+        #TODO: What are we doing with `out`?
+        (d,m) = (self._data, self._mask)
+        if m is nomask:
+            return d.trace(offset=offset, axis1=axis1, axis2=axis2,
+                           out=out).astype(dtype)
+        else:
+            D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2,
+                              ).astype(dtype)
+            return D.sum(axis=None)
+    #............................................
+    def sum(self, axis=None, dtype=None):
+        """a.sum(axis=None, dtype=None)
+Sums the array `a` over the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+    """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.sum(None, dtype=dtype)
+            return self.__class__(self._data.sum(axis, dtype=dtype),
+                                  mask=nomask, fill_value=self.fill_value,
+                                  **self.options)
+        else:
+#            if axis is None:
+#                return self.filled(0).sum(None, dtype=dtype)
+            return self.__class__(self.filled(0).sum(axis, dtype=dtype),
+                                  mask=self._mask.all(axis),
+                                  fill_value=self.fill_value, **self.options)
+
+    def cumsum(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative sum of the elements of array `a` along the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.cumsum(None, dtype=dtype)
+            return self.__class__(self._data.cumsum(axis=axis, dtype=dtype),
+                                  fill_value=self.fill_value, **self.options)
+        else:
+#            if axis is None:
+#                return self.filled(0).cumsum(None, dtype=dtype)
+            return self.__class__(self.filled(0).cumsum(axis=axis, dtype=dtype),
+                                  mask=self._mask, fill_value=self.fill_value,
+                                  **self.options)
+
+    def prod(self, axis=None, dtype=None):
+        """a.prod(axis=None, dtype=None)
+Returns the product of the elements of array `a` along the given axis `axis`.
+Masked elements are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.prod(None, dtype=dtype)
+            return self.__class__(self._data.prod(axis, dtype=dtype),
+                                  mask=nomask, fill_value=self.fill_value,
+                                  **self.options)
+#            return self.__class__(self._data.prod(axis=axis, dtype=dtype))
+        else:
+#            if axis is None:
+#                return self.filled(1).prod(None, dtype=dtype)
+            return self.__class__(self.filled(1).prod(axis=axis, dtype=dtype),
+                                  mask=self._mask.all(axis),
+                                  fill_value=self.fill_value,
+                                  **self.options)
+    product = prod
+
+    def cumprod(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative product of ethe lements of array `a` along the given axis `axis`.
+Masked values are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.cumprod(None, dtype=dtype)
+            return self.__class__(self._data.cumprod(axis=axis, dtype=dtype),
+                                  mask=nomask, fill_value=self.fill_value,
+                                  **self.options)
+        else:
+#            if axis is None:
+#                return self.filled(1).cumprod(None, dtype=dtype)
+            return self.__class__(self.filled(1).cumprod(axis=axis, dtype=dtype),
+                                  mask=self._mask,
+                                  fill_value=self.fill_value, **self.options)
+
+    def mean(self, axis=None, dtype=None):
+        """a.mean(axis=None, dtype=None)
+
+    Averages the array over the given axis.  If the axis is None,
+    averages over all dimensions of the array.  Equivalent to
+
+      a.sum(axis, dtype) / size(a, axis).
+
+    The optional dtype argument is the data type for intermediate
+    calculations in the sum.
+
+    Returns a masked array, of the same class as a.
+        """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.mean(axis=None, dtype=dtype)
+            return self.__class__(self._data.mean(axis=axis, dtype=dtype),
+                                  mask=nomask, fill_value=self.fill_value,
+                                  **self.options)
+        else:
+            dsum = fromnumeric.sum(self.filled(0), axis=axis, dtype=dtype)
+            cnt = self.count(axis=axis)
+            mask = self._mask.all(axis)
+            if axis is None and mask:
+                return masked
+            return self.__class__(dsum*1./cnt, mask=mask,
+                                  fill_value=self.fill_value, **self.options)
+
+    def anom(self, axis=None, dtype=None):
+        """a.anom(axis=None, dtype=None)
+    Returns the anomalies, or deviation from the average.
+            """
+        m = self.mean(axis, dtype)
+        if not axis:
+            return (self - m)
+        else:
+            return (self - expand_dims(m,axis))
+
+    def var(self, axis=None, dtype=None):
+        """a.var(axis=None, dtype=None)
+Returns the variance, a measure of the spread of a distribution.
+
+The variance is the average of the squared deviations from the mean,
+i.e. var = mean((x - x.mean())**2).
+        """
+        if self._mask is nomask:
+#            if axis is None:
+#                return self._data.var(axis=None, dtype=dtype)
+            return self.__class__(self._data.var(axis=axis, dtype=dtype),
+                                  mask=nomask,
+                                  fill_value=self.fill_value, **self.options)
+        else:
+            cnt = self.count(axis=axis)
+            danom = self.anom(axis=axis, dtype=dtype)
+            danom *= danom
+            dvar = danom.sum(axis) / cnt
+#            dvar /= cnt
+            if axis is None:
+                return dvar
+            return self.__class__(dvar,
+                                  mask=mask_or(self._mask.all(axis), (cnt==1)),
+                                  fill_value=self.fill_value, **self.options)
+
+    def std(self, axis=None, dtype=None):
+        """a.std(axis=None, dtype=None)
+Returns the standard deviation, a measure of the spread of a distribution.
+
+The standard deviation is the square root of the average of the squared
+deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
+        """
+        dvar = self.var(axis,dtype)
+        if axis is None:
+            if dvar is masked:
+                return masked
+            else:
+                # Should we use umath.sqrt instead ?
+                return sqrt(dvar)
+        return self.__class__(sqrt(dvar._data), mask=dvar._mask,
+                              dtype = self.dtype,
+                              fill_value=self.fill_value, **self.options)
+    #............................................
+    def argsort(self, axis=None, fill_value=None, kind='quicksort',
+                order=None):
+        """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self._data)
+        d = self.filled(fill_value)
+        if axis is None:
+            return d.argsort(kind=kind, order=order)
+        return d.argsort(axis, kind=kind, order=order)
+
+    def argmin(self, axis=None, fill_value=None):
+        """Returns the array of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self._data)
+        d = self.filled(fill_value)
+        if axis is None:
+            return d.argmin()
+        return d.argmin(axis)
+
+    def argmax(self, axis=None, fill_value=None):
+        """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self._data)
+            try:
+                fill_value = - fill_value
+            except:
+                pass
+        d = self.filled(fill_value)
+        if axis is None:
+            return d.argmax()
+        return d.argmax(axis)
+
+    def sort(self, axis=-1, kind='quicksort', order=None, endwith=True):
+        """
+        Sort a along the given axis.
+
+    Keyword arguments:
+
+    axis  -- axis to be sorted (default -1)
+    kind  -- sorting algorithm (default 'quicksort')
+             Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+    order -- If a has fields defined, then the order keyword can be the
+             field name to sort on or a list (or tuple) of field names
+             to indicate the order that fields should be used to define
+             the sort.
+    endwith--Boolean flag indicating whether missing values (if any) should
+             be forced in the upper indices (at the end of the array) or
+             lower indices (at the beginning).
+
+    Returns: None.
+
+    This method sorts 'a' in place along the given axis using the algorithm
+    specified by the kind keyword.
+
+    The various sorts may characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order and is most
+    useful when used with argsort where the key might differ from the items
+    being sorted. The three available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is
+    not along the last axis. Consequently, sorts along the last axis are faster
+    and use less space than sorts along other axis.
+
+    """
+
+        if endwith:
+            filler = minimum_fill_value(self.dtype)
+        else:
+            filler = maximum_fill_value(self.dtype)
+        indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
+        self._data = self._data[indx]
+        m = self._mask
+        if m is not nomask:
+            self._mask = m[indx]
+        return
+    #............................................
+    # Backwards Compatibility. Heck...
+    @property
+    def data(self):
+        """Returns the `_data` part of the MaskedArray.
+You should really use `_data` instead..."""
+        return self._data
+    def raw_data(self):
+        """Returns the `_data` part of the MaskedArray.
+You should really use `_data` instead..."""
+        return self._data
+
+##..............................................................................
+
+
+
+#class _arithmethods:
+#    """Defines a wrapper for arithmetic methods.
+#Instead of directly calling a ufunc, the corresponding method of  the `array._data`
+#object is called instead.
+#    """
+#    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+#        """
+#:Parameters:
+#    - `methodname` (String) : Method name.
+#    - `fill_self` (Float *[0]*) : Fill value for the instance.
+#    - `fill_other` (Float *[0]*) : Fill value for the target.
+#    - `domain` (Domain object *[None]*) : Domain of non-validity.
+#        """
+#        self.methodname = methodname
+#        self.fill_self = fill_self
+#        self.fill_other = fill_other
+#        self.domain = domain
+#    #
+#    def __call__ (self, instance, other, *args):
+#        "Execute the call behavior."
+#        m_self = instance._mask
+#        m_other = getmask(other)
+#        base = filled(instance,self.fill_self)
+#        target = filled(other, self.fill_other)
+#        if self.domain is not None:
+#            # We need to force the domain to a ndarray only.
+#            if self.fill_other > self.fill_self:
+#                domain = self.domain(base, target)
+#            else:
+#                domain = self.domain(target, base)
+#            if domain.any():
+#                #If `other` is a subclass of ndarray, `filled` must have the
+#                # same subclass, else we'll lose some info.
+#                #The easiest then is to fill `target` instead of creating
+#                # a pure ndarray.
+#                #Oh, and we better make a copy!
+#                if isinstance(other, ndarray):
+#                    if target is other:
+#                        # We don't want to modify other: let's copy target, then
+#                        target = target.copy()
+#                    target[:] = numeric.where(fromnumeric.asarray(domain),
+#                                              self.fill_other, target)
+#                else:
+#                    target = numeric.where(fromnumeric.asarray(domain),
+#                                           self.fill_other, target)
+#                m_other = mask_or(m_other, domain)
+#        m = mask_or(m_self, m_other)
+#        method = getattr(base, self.methodname)
+#        return instance.__class__(method(target, *args), mask=m)
+#    #
+#    def patch(self):
+#        """Applies the method `func` from class `method` to MaskedArray"""
+#        return types.MethodType(self,None,MaskedArray)
+#..............................................................................
+class _arithmethods(object):
+    """Defines a wrapper for arithmetic methods.
+Instead of directly calling a ufunc, the corresponding method of  the `array._data`
+object is called instead.
+    """
+    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+        """
+:Parameters:
+    - `methodname` (String) : Method name.
+    - `fill_self` (Float *[0]*) : Fill value for the instance.
+    - `fill_other` (Float *[0]*) : Fill value for the target.
+    - `domain` (Domain object *[None]*) : Domain of non-validity.
+        """
+        self.methodname = methodname
+        self.fill_self = fill_self
+        self.fill_other = fill_other
+        self.domain = domain
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self.methodname).__doc__
+        except:
+            return getattr(numpy, self.methodname).__doc__
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__ (self, other, *args):
+        "Execute the call behavior."
+        instance = self.obj
+        m_self = instance._mask
+        m_other = getmask(other)
+        base = filled(instance,self.fill_self)
+        target = filled(other, self.fill_other)
+        if self.domain is not None:
+            # We need to force the domain to a ndarray only.
+            if self.fill_other > self.fill_self:
+                domain = self.domain(base, target)
+            else:
+                domain = self.domain(target, base)
+            if domain.any():
+                #If `other` is a subclass of ndarray, `filled` must have the
+                # same subclass, else we'll lose some info.
+                #The easiest then is to fill `target` instead of creating
+                # a pure ndarray.
+                #Oh, and we better make a copy!
+                if isinstance(other, ndarray):
+                    if target is other or target is base:
+                        # We don't want to modify other: let's copy target, then
+                        # Same if target us base, instead...
+                        target = target.copy()
+                    target[:] = numeric.where(fromnumeric.asarray(domain),
+                                              self.fill_other, target)
+                else:
+                    target = numeric.where(fromnumeric.asarray(domain),
+                                           self.fill_other, target)
+                m_other = mask_or(m_other, domain)
+        m = mask_or(m_self, m_other)
+        method = getattr(base, self.methodname)
+        return instance.__class__(method(target, *args), mask=m,
+                                  fill_value=instance.fill_value,
+                                  **instance.options)
+#......................................
+class _compamethods(object):
+    """Defines comparison methods (eq, ge, gt...).
+Instead of calling a ufunc, the method of the masked object is called.
+    """
+    def __init__ (self, methodname, fill_self=0, fill_other=0):
+        """
+:Parameters:
+    - `methodname` (String) : Method name.
+    - `fill_self` (Float *[0]*) : Fill value for the instance.
+    - `fill_other` (Float *[0]*) : Fill value for the target.
+    - `domain` (Domain object *[None]*) : Domain of non-validity.
+        """
+        self.methodname = methodname
+        self.fill_self = fill_self
+        self.fill_other = fill_other
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self.methodname).__doc__
+        except:
+            return getattr(numpy, self.methodname).__doc__
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__ (self, other, *args):
+        "Execute the call behavior."
+        instance = self.obj
+        m = mask_or(instance._mask, getmask(other), small_mask=False)
+        base = instance.filled(self.fill_self)
+        target = filled(other, self.fill_other)
+        method = getattr(base, self.methodname)
+        return instance.__class__(method(target, *args), mask=m,
+                                  **instance.options)
+#..........................................................
+MaskedArray.__add__ = _arithmethods('__add__')
+MaskedArray.__radd__ = _arithmethods('__add__')
+MaskedArray.__sub__ = _arithmethods('__sub__')
+MaskedArray.__rsub__ = _arithmethods('__rsub__')
+MaskedArray.__pow__ = _arithmethods('__pow__')
+MaskedArray.__mul__ = _arithmethods('__mul__', 1, 1)
+MaskedArray.__rmul__ = _arithmethods('__mul__', 1, 1)
+MaskedArray.__div__ = _arithmethods('__div__', 0, 1,
+                                    domain_safe_divide())
+MaskedArray.__rdiv__ = _arithmethods('__rdiv__', 1, 0,
+                                     domain_safe_divide())
+MaskedArray.__truediv__ = _arithmethods('__truediv__', 0, 1,
+                                        domain_safe_divide())
+MaskedArray.__rtruediv__ = _arithmethods('__rtruediv__', 1, 0,
+                                         domain_safe_divide())
+MaskedArray.__floordiv__ = _arithmethods('__floordiv__', 0, 1,
+                                         domain_safe_divide())
+MaskedArray.__rfloordiv__ = _arithmethods('__rfloordiv__', 1, 0,
+                                          domain_safe_divide())
+MaskedArray.__eq__ = _compamethods('__eq__')
+MaskedArray.__ne__ = _compamethods('__ne__')
+MaskedArray.__le__ = _compamethods('__le__')
+MaskedArray.__lt__ = _compamethods('__lt__')
+MaskedArray.__ge__ = _compamethods('__ge__')
+MaskedArray.__gt__ = _compamethods('__gt__')
+#####--------------------------------------------------------------------------
+#---- --- Shortcuts ---
+#####---------------------------------------------------------------------------
+def isMaskedArray (x):
+    "Is x a masked array, that is, an instance of MaskedArray?"
+    return isinstance(x, MaskedArray)
+isarray = isMaskedArray
+isMA = isMaskedArray  #backward compatibility
+#masked = MaskedArray(0, int, mask=1)
+masked_singleton = MaskedArray(0, dtype=int_, mask=True)
+masked = masked_singleton
+
+masked_array = MaskedArray
+def array(data, dtype=None, copy=False, order=False, mask=nomask,
+          keep_mask=True, small_mask=True, hard_mask=None, fill_value=None):
+    """array(data, dtype=None, copy=True, order=False, mask=nomask,
+             keep_mask=True, small_mask=True, fill_value=None)
+Acts as shortcut to MaskedArray, with options in a different order for convenience.
+And backwards compatibility...
+    """
+    return MaskedArray(data, mask=mask, dtype=dtype, copy=copy,
+                       keep_mask=keep_mask, small_mask=small_mask,
+                       hard_mask=hard_mask, fill_value=fill_value)
+
+def is_masked(x):
+    """Returns whether x has some masked values."""
+    m = getmask(x)
+    if m is nomask:
+        return False
+    elif m.any():
+        return True
+    return False
+
+
+#####--------------------------------------------------------------------------
+#---- --- Patch methods ---
+#####--------------------------------------------------------------------------
+#class _arraymethod:
+#    """Defines a wrapper for basic array methods.
+#Upon call, returns a masked array, where the new `_data` array is the output
+#of the corresponding method called on the original `_data`.
+#
+#If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+#If `onmask` is False, the new mask is just a reference to the initial mask.
+#
+#:Parameters:
+#    `funcname` : String
+#        Name of the function to apply on data.
+#    `onmask` : Boolean *[True]*
+#        Whether the mask must be processed also (True) or left alone (False).
+#    """
+#    def __init__(self, funcname, onmask=True):
+#        self._name = funcname
+#        self._onmask = onmask
+#        self.__doc__ = getattr(ndarray, self._name).__doc__
+#    def __call__(self, instance, *args, **params):
+#        methodname = self._name
+#        (d,m) = (instance._data, instance._mask)
+#        C = instance.__class__
+#        if m is nomask:
+#            return C(getattr(d,methodname).__call__(*args, **params))
+#        elif self._onmask:
+#            return C(getattr(d,methodname).__call__(*args, **params),
+#                     mask=getattr(m,methodname)(*args, **params) )
+#        else:
+#            return C(getattr(d,methodname).__call__(*args, **params), mask=m)
+#
+#    def patch(self):
+#        "Adds the new method to MaskedArray."
+#        return types.MethodType(self, None, MaskedArray)
+##......................................
+#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch()
+#MaskedArray.diagonal = _arraymethod('diagonal').patch()
+#MaskedArray.take = _arraymethod('take').patch()
+#MaskedArray.ravel = _arraymethod('ravel').patch()
+#MaskedArray.transpose = _arraymethod('transpose').patch()
+#MaskedArray.T = _arraymethod('transpose').patch()
+#MaskedArray.swapaxes = _arraymethod('swapaxes').patch()
+#MaskedArray.clip = _arraymethod('clip', onmask=False).patch()
+#MaskedArray.compress = _arraymethod('compress').patch()
+#MaskedArray.resize = _arraymethod('resize').patch()
+#MaskedArray.copy = _arraymethod('copy').patch()
+
+class _arraymethod(object):
+    """Defines a wrapper for basic array methods.
+Upon call, returns a masked array, where the new `_data` array is the output
+of the corresponding method called on the original `_data`.
+
+If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+If `onmask` is False, the new mask is just a reference to the initial mask.
+
+:Parameters:
+    `funcname` : String
+        Name of the function to apply on data.
+    `onmask` : Boolean *[True]*
+        Whether the mask must be processed also (True) or left alone (False).
+    """
+    def __init__(self, funcname, onmask=True):
+        self._name = funcname
+        self._onmask = onmask
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self._name).__doc__
+        except:
+            return getattr(numpy, self._name).__doc__
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__(self, *args, **params):
+        methodname = self._name
+        obj = self.obj
+        (d, m) = (obj._data, obj._mask)
+        (t, f) = (obj.dtype, obj._fill_value)
+        C = self.obj.__class__
+        if m is nomask:
+            return C(getattr(d,methodname).__call__(*args, **params),
+                     dtype=t, fill_value=f)
+        elif self._onmask:
+            return C(getattr(d,methodname).__call__(*args, **params),
+                     mask=getattr(m,methodname)(*args, **params),
+                     dtype=t, fill_value=f, **obj.options)
+        else:
+            return C(getattr(d,methodname).__call__(*args, **params), mask=m,
+                     dtype=t, fill_value=f, **obj.options)
+#......................................
+MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate')
+MaskedArray.copy = _arraymethod('copy')
+MaskedArray.diagonal = _arraymethod('diagonal')
+MaskedArray.take = _arraymethod('take')
+MaskedArray.ravel = _arraymethod('ravel')
+MaskedArray.transpose = _arraymethod('transpose')
+MaskedArray.T = property(fget=lambda self:self.transpose())
+MaskedArray.swapaxes = _arraymethod('swapaxes')
+MaskedArray.clip = _arraymethod('clip', onmask=False)
+MaskedArray.compress = _arraymethod('compress')
+MaskedArray.copy = _arraymethod('copy')
+MaskedArray.squeeze = _arraymethod('squeeze')
+
+#####--------------------------------------------------------------------------
+#---- --- Extrema functions ---
+#####--------------------------------------------------------------------------
+class _minimum_operation:
+    "Object to calculate minima"
+    def __init__ (self):
+        """minimum(a, b) or minimum(a)
+In one argument case, returns the scalar minimum.
+        """
+        pass
+    #.........
+    def __call__ (self, a, b=None):
+        "Execute the call behavior."
+        if b is None:
+            m = getmask(a)
+            if m is nomask:
+                d = amin(filled(a).ravel())
+                return d
+            ac = a.compressed()
+            if len(ac) == 0:
+                return masked
+            else:
+                return amin(ac)
+        else:
+            return where(less(a, b), a, b)
+    #.........
+    def reduce(self, target, axis=0):
+        """Reduces `target` along the given `axis`."""
+        m = getmask(target)
+        if m is nomask:
+            t = filled(target)
+            return masked_array (umath.minimum.reduce (t, axis))
+        else:
+            t = umath.minimum.reduce(filled(target, minimum_fill_value(target)),
+                                     axis)
+            m = umath.logical_and.reduce(m, axis)
+#            return masked_array(t, mask=m, fill_value=get_fill_value(target))
+            try:
+                return target.__class__(t, mask=m, dtype=t.dtype,
+                                        fill_value=get_fill_value(target))
+            except AttributeError:
+                return masked_array(t, mask=m, dtype=t.dtype,
+                                    fill_value=get_fill_value(target))
+    #.........
+    def outer(self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = logical_or.outer(ma, mb)
+        d = umath.minimum.outer(filled(a), filled(b))
+        return masked_array(d, mask=m)
+
+def min(array, axis=None, out=None):
+    """Returns the minima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return minimum(array)
+    else:
+        return minimum.reduce(array, axis)
+#................................................
+class _maximum_operation:
+    "Object to calculate maxima"
+    def __init__ (self):
+        """maximum(a, b) or maximum(a)
+           In one argument case returns the scalar maximum.
+        """
+        pass
+    #.........
+    def __call__ (self, a, b=None):
+        "Executes the call behavior."
+        if b is None:
+            m = getmask(a)
+            if m is nomask:
+                d = amax(filled(a).ravel())
+                return d
+            ac = a.compressed()
+            if len(ac) == 0:
+                return masked
+            else:
+                return amax(ac)
+        else:
+            return where(greater(a, b), a, b)
+    #.........
+    def reduce (self, target, axis=0):
+        """Reduces target along the given axis."""
+        m = getmask(target)
+        if m is nomask:
+            t = filled(target)
+            return masked_array(umath.maximum.reduce (t, axis))
+        else:
+            t = umath.maximum.reduce(filled(target, maximum_fill_value(target)),
+                                     axis)
+            m = umath.logical_and.reduce(m, axis)
+            try:
+                return target.__class__(t, mask=m, dtype=t.dtype,
+                                        fill_value=get_fill_value(target))
+            except AttributeError:
+                return masked_array(t, mask=m, dtype=t.dtype,
+                                    fill_value=get_fill_value(target))
+    #.........
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = logical_or.outer(ma, mb)
+        d = umath.maximum.outer(filled(a), filled(b))
+        return masked_array(d, mask=m)
+
+def max(obj, axis=None, out=None):
+    """Returns the maxima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return maximum(obj)
+    else:
+        return maximum.reduce(obj, axis)
+#................................................
+def ptp(obj, axis=None):
+    """a.ptp(axis=None) =  a.max(axis)-a.min(axis)"""
+    try:
+        return obj.max(axis)-obj.min(axis)
+    except AttributeError:
+        return max(obj, axis=axis) - min(obj, axis=axis)
+#................................................
+MaskedArray.min = min
+MaskedArray.max = max
+MaskedArray.ptp = ptp
+
+#####---------------------------------------------------------------------------
+#---- --- Definition of functions from the corresponding methods ---
+#####---------------------------------------------------------------------------
+class _frommethod:
+    """Defines functions from existing MaskedArray methods.
+:ivar _methodname (String): Name of the method to transform.
+    """
+    def __init__(self, methodname):
+        self._methodname = methodname
+        self.__doc__ = self.getdoc()
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self._methodname).__doc__
+        except:
+            return getattr(numpy, self._methodname).__doc__
+    def __call__(self, x, *args, **params):
+        if isinstance(x, MaskedArray):
+            return getattr(x, self._methodname).__call__(*args, **params)
+        #FIXME: As x is not a MaskedArray, we transform it to a ndarray with asarray
+        #FIXME: ... and call the corresponding method.
+        #FIXME: Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
+        #FIXME: we end up with a "SystemError: NULL result without error in PyObject_Call"
+        #FIXME: A dirty trick is then to call the initial numpy function...
+        method = getattr(fromnumeric.asarray(x), self._methodname)
+        try:
+            return method(*args, **params)
+        except SystemError:
+            return getattr(numpy,self._methodname).__call__(x, *args, **params)
+
+all = _frommethod('all')
+anomalies = anom = _frommethod('anom')
+any = _frommethod('any')
+conjugate = _frommethod('conjugate')
+ids = _frommethod('ids')
+nonzero = _frommethod('nonzero')
+diagonal = _frommethod('diagonal')
+maximum = _maximum_operation()
+mean = _frommethod('mean')
+minimum = _minimum_operation ()
+product = _frommethod('prod')
+ptp = _frommethod('ptp')
+ravel = _frommethod('ravel')
+repeat = _frommethod('repeat')
+std = _frommethod('std')
+sum = _frommethod('sum')
+swapaxes = _frommethod('swapaxes')
+take = _frommethod('take')
+var = _frommethod('var')
+
+#..............................................................................
+def power(a, b, third=None):
+    """Computes a**b elementwise.
+    Masked values are set to 1."""
+    if third is not None:
+        raise MAError, "3-argument power not supported."
+    ma = getmask(a)
+    mb = getmask(b)
+    m = mask_or(ma, mb)
+    fa = filled(a, 1)
+    fb = filled(b, 1)
+    if fb.dtype.char in typecodes["Integer"]:
+        return masked_array(umath.power(fa, fb), m)
+    md = make_mask((fa < 0), small_mask=1)
+    m = mask_or(m, md)
+    if m is nomask:
+        return masked_array(umath.power(fa, fb))
+    else:
+        fa[m] = 1
+        return masked_array(umath.power(fa, fb), m)
+
+#..............................................................................
+def argsort(a, axis=None, kind='quicksort', fill_value=None):
+    """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argsort(kind=kind)
+    return d.argsort(axis, kind)
+
+def argmin(a, axis=None, fill_value=None):
+    """Returns the array of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argmin(axis=None)
+    return d.argmin(axis=axis)
+
+def argmax(a, axis=None, fill_value=None):
+    """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+        try:
+            fill_value = - fill_value
+        except:
+            pass
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argmax(axis=None)
+    return d.argmax(axis=axis)
+
+def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+    """
+    Sort a along the given axis.
+
+Keyword arguments:
+
+axis  -- axis to be sorted (default -1)
+kind  -- sorting algorithm (default 'quicksort')
+         Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+order -- If a has fields defined, then the order keyword can be the
+         field name to sort on or a list (or tuple) of field names
+         to indicate the order that fields should be used to define
+         the sort.
+endwith--Boolean flag indicating whether missing values (if any) should
+         be forced in the upper indices (at the end of the array) or
+         lower indices (at the beginning).
+
+Returns: None.
+
+This method sorts 'a' in place along the given axis using the algorithm
+specified by the kind keyword.
+
+The various sorts may characterized by average speed, worst case
+performance, need for work space, and whether they are stable. A stable
+sort keeps items with the same key in the same relative order and is most
+useful when used with argsort where the key might differ from the items
+being sorted. The three available algorithms have the following properties:
+
+|------------------------------------------------------|
+|    kind   | speed |  worst case | work space | stable|
+|------------------------------------------------------|
+|'quicksort'|   1   | O(n^2)      |     0      |   no  |
+|'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+|'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+|------------------------------------------------------|
+
+All the sort algorithms make temporary copies of the data when the sort is
+not along the last axis. Consequently, sorts along the last axis are faster
+and use less space than sorts along other axis.
+
+"""
+    a = numeric.asanyarray(a)
+    if fill_value is None:
+        if endwith:
+            filler = minimum_fill_value(a)
+        else:
+            filler = maximum_fill_value(a)
+    else:
+        filler = fill_value
+#    return
+    indx = numpy.indices(a.shape).tolist()
+    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+    return a[indx]
+
+def compressed(x):
+    """Returns a compressed version of a masked array (or just the array if it
+    wasn't masked first)."""
+    if getmask(x) is None:
+        return x
+    else:
+        return x.compressed()
+
+def count(a, axis = None):
+    "Count of the non-masked elements in a, or along a certain axis."
+    a = masked_array(a)
+    return a.count(axis)
+
+def concatenate(arrays, axis=0):
+    "Concatenates the arrays along the given axis"
+    #TODO: We lose the subclass, here! We should keep track of the classes...
+    #TODO: ...and find the max ? the lowest according to MRO?
+    d = []
+    for x in arrays:
+        d.append(filled(x))
+    d = numeric.concatenate(d, axis)
+    for x in arrays:
+        if getmask(x) is not nomask:
+            break
+    else:
+        return masked_array(d)
+    dm = []
+    for x in arrays:
+        dm.append(getmaskarray(x))
+    dm = make_mask(numeric.concatenate(dm, axis), copy=False, small_mask=True)
+    return masked_array(d, mask=dm)
+
+def expand_dims(x,axis):
+    """Expand the shape of a by including newaxis before given axis."""
+    if isinstance(x, MaskedArray):
+        (d,m) = (x._data, x._mask)
+        if m is nomask:
+            return masked_array(n_expand_dims(d,axis),
+                                dtype=d.dtype, fill_value=x._fill_value)
+        else:
+            return masked_array(n_expand_dims(d,axis),
+                                mask=n_expand_dims(m,axis),
+                                dtype=d.dtype, fill_value=x._fill_value)
+    else:
+        return n_expand_dims(x,axis)
+
+#......................................
+def left_shift (a, n):
+    "Left shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.left_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.left_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+
+def right_shift (a, n):
+    "Right shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.right_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.right_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+#......................................
+def put(a, indices, values, mode='raise'):
+    """Sets storage-indexed locations to corresponding values.
+    Values and indices are filled if necessary."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.put(indices, values, mode=mode)
+    except AttributeError:
+        return fromnumeric.asarray(a).put(indices, values, mode=mode)
+
+def putmask(a, mask, values): #, mode='raise'):
+    """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true.
+If `v` is shorter than `mask`, it will be repeated as necessary.
+In particular `v` can be a scalar or length 1 array."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.putmask(values, mask)
+    except AttributeError:
+        return fromnumeric.asarray(a).putmask(values, mask)
+
+def transpose(a,axes=None):
+    """Returns a view of the array with dimensions permuted according to axes.
+If `axes` is None (default), returns array with dimensions reversed.
+    """
+    #We can't use 'frommethod', as 'transpose' doesn't take keywords
+    try:
+        return a.transpose(axes)
+    except AttributeError:
+        return fromnumeric.asarray(a).transpose(axes)
+
+def reshape(a, new_shape):
+    """Changes the shape of the array `a` to `new_shape`."""
+    #We can't use 'frommethod', it whine about some parameters. Dmmit.
+    try:
+        return a.reshape(new_shape)
+    except AttributeError:
+        return fromnumeric.asarray(a).reshape(new_shape)
+
+def resize(x, new_shape):
+    """resize(a,new_shape) returns a new array with the specified shape.
+    The total size of the original array can be any size.
+    The new array is filled with repeated copies of a. If a was masked, the new
+    array will be masked, and the new mask will be a repetition of the old one.
+    """
+    # We can't use _frommethods here, as N.resize is notoriously whiny.
+    m = getmask(x)
+    if m is not nomask:
+        m = fromnumeric.resize(m, new_shape)
+    if isinstance(x, MaskedArray):
+        result = x.__class__(fromnumeric.resize(filled(x), new_shape), mask=m)
+    else:
+        result = masked_array(fromnumeric.resize(filled(x), new_shape), mask=m)
+    result.set_fill_value(get_fill_value(x))
+    return result
+
+
+#................................................
+def rank(obj):
+    """Gets the rank of sequence a (the number of dimensions, not a matrix rank)
+The rank of a scalar is zero."""
+    return fromnumeric.rank(filled(obj))
+#
+def shape(obj):
+    """Returns the shape of `a` (as a function call which also works on nested sequences).
+    """
+    return fromnumeric.shape(filled(obj))
+#
+def size(obj, axis=None):
+    """Returns the number of elements in the array along the given axis,
+or in the sequence if `axis` is None.
+    """
+    return fromnumeric.size(filled(obj), axis)
+#................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Extra functions ---
+#####--------------------------------------------------------------------------
+def where (condition, x, y):
+    """where(condition, x, y) is x where condition is nonzero, y otherwise.
+       condition must be convertible to an integer array.
+       Answer is always the shape of condition.
+       The type depends on x and y. It is integer if both x and y are
+       the value masked.
+    """
+    fc = filled(not_equal(condition, 0), 0)
+    xv = filled(x)
+    xm = getmask(x)
+    yv = filled(y)
+    ym = getmask(y)
+    d = numeric.choose(fc, (yv, xv))
+    md = numeric.choose(fc, (ym, xm))
+    m = getmask(condition)
+    m = make_mask(mask_or(m, md), copy=False, small_mask=True)
+    return masked_array(d, mask=m)
+
+def choose (indices, t, out=None, mode='raise'):
+    "Returns array shaped like indices with elements chosen from t"
+    #TODO: implement options `out` and `mode`, if possible.
+    def fmask (x):
+        "Returns the filled array, or True if ``masked``."
+        if x is masked:
+            return 1
+        return filled(x)
+    def nmask (x):
+        "Returns the mask, True if ``masked``, False if ``nomask``."
+        if x is masked:
+            return 1
+        m = getmask(x)
+        if m is nomask:
+            return 0
+        return m
+    c = filled(indices, 0)
+    masks = [nmask(x) for x in t]
+    a = [fmask(x) for x in t]
+    d = numeric.choose(c, a)
+    m = numeric.choose(c, masks)
+    m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1)
+    return masked_array(d, mask=m)
+
+def round_(a, decimals=0, out=None):
+    """Returns reference to result. Copies a and rounds to 'decimals' places.
+
+    Keyword arguments:
+        decimals -- number of decimals to round to (default 0). May be negative.
+        out -- existing array to use for output (default copy of a).
+
+    Return:
+        Reference to out, where None specifies a copy of the original array a.
+
+    Round to the specified number of decimals. When 'decimals' is negative it
+    specifies the number of positions to the left of the decimal point. The
+    real and imaginary parts of complex numbers are rounded separately.
+    Nothing is done if the array is not of float type and 'decimals' is greater
+    than or equal to 0."""
+    if not hasattr(a, "_mask"):
+        mask = nomask
+    else:
+        mask = a._mask
+    if out is None:
+        return a.__class__(fromnumeric.round_(a, decimals, None), mask=mask)
+    else:
+        out = a.__class__(fromnumeric.round_(a, decimals, out), mask=mask)
+        return out
+
+def arange(start, stop=None, step=1, dtype=None):
+    """Just like range() except it returns a array whose type can be specified
+    by the keyword argument dtype.
+    """
+    return array(numeric.arange(start, stop, step, dtype), mask=nomask)
+
+def inner(a, b):
+    """inner(a,b) returns the dot product of two arrays, which has
+    shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the
+    product of the elements from the last dimensions of a and b.
+    Masked elements are replace by zeros.
+    """
+    fa = filled(a, 0)
+    fb = filled(b, 0)
+    if len(fa.shape) == 0:
+        fa.shape = (1,)
+    if len(fb.shape) == 0:
+        fb.shape = (1,)
+    return masked_array(numeric.inner(fa, fb))
+innerproduct = inner
+
+def outer(a, b):
+    """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))"""
+    fa = filled(a, 0).ravel()
+    fb = filled(b, 0).ravel()
+    d = numeric.outer(fa, fb)
+    ma = getmask(a)
+    mb = getmask(b)
+    if ma is nomask and mb is nomask:
+        return masked_array(d)
+    ma = getmaskarray(a)
+    mb = getmaskarray(b)
+    m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
+    return masked_array(d, mask=m)
+outerproduct = outer
+
+def allequal (a, b, fill_value=True):
+    """
+Returns `True` if all entries of  a and b are equal, using
+fill_value as a truth value where either or both are masked.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    if m is nomask:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        return d.all()
+    elif fill_value:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        dm = array(d, mask=m, copy=False)
+        return dm.filled(True).all(None)
+    else:
+        return False
+
+def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+    """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances.
+If `fill_value` is True, masked values are considered equal.
+If `fill_value` is False, masked values considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error `atol` comes into play for those elements of `b`
+ that are very small or zero; it says how small `a` must be also.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    d1 = filled(a)
+    d2 = filled(b)
+    x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
+    y = filled(array(d2, copy=0, mask=m), 1).astype(float)
+    d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+    return fromnumeric.alltrue(fromnumeric.ravel(d))
+
+#..............................................................................
+def asarray(a, dtype=None):
+    """asarray(data, dtype) = array(data, dtype, copy=0)
+Returns `a` as an masked array.
+No copy is performed if `a` is already an array.
+Subclasses are converted to base class MaskedArray.
+    """
+    return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
+
+def empty(new_shape, dtype=float):
+    """empty((d1,...,dn),dtype=float,order='C')
+Returns a new array of shape (d1,...,dn) and given type with all its
+entries uninitialized. This can be faster than zeros."""
+    return masked_array(numeric.empty(new_shape, dtype), mask=nomask)
+
+def empty_like(a):
+    """empty_like(a)
+Returns an empty (uninitialized) array of the shape and typecode of a.
+Note that this does NOT initialize the returned array.
+If you require your array to be initialized, you should use zeros_like()."""
+    return masked_array(numeric.empty_like(a), mask=nomask)
+
+def ones(new_shape, dtype=float):
+    """ones(shape, dtype=None)
+Returns an array of the given dimensions, initialized to all ones."""
+    return masked_array(numeric.ones(new_shape, dtype), mask=nomask)
+
+def zeros(new_shape, dtype=float):
+    """zeros(new_shape, dtype=None)
+Returns an array of the given dimensions, initialized to all zeros."""
+    return masked_array(numeric.zeros(new_shape, dtype), mask=nomask)
+
+#####--------------------------------------------------------------------------
+#---- --- Pickling ---
+#####--------------------------------------------------------------------------
+#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data
+def _mareconstruct(subtype, baseshape, basetype,):
+    """Internal function that builds a new MaskedArray from the information stored
+in a pickle."""
+    _data = ndarray.__new__(ndarray, baseshape, basetype)
+    _mask = ndarray.__new__(ndarray, baseshape, basetype)
+    return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False)
+
+def _getstate(a):
+    "Returns the internal state of the masked array, for pickling purposes."
+    state = (1,
+             a.shape,
+             a.dtype,
+             a.flags.fnc,
+             (a._data).__reduce__()[-1][-1],
+             getmaskarray(a).__reduce__()[-1][-1])
+    return state
+
+def _setstate(a, state):
+    """Restores the internal state of the masked array, for pickling purposes.
+`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
+
+    - class name
+    - a tuple giving the shape of the data
+    - a typecode for the data
+    - a binary string for the data
+    - a binary string for the mask.
+        """
+    (ver, shp, typ, isf, raw, msk) = state
+    (a._data).__setstate__((shp, typ, isf, raw))
+    (a._mask).__setstate__((shp, dtype('|b1'), isf, msk))
+
+def _reduce(a):
+    """Returns a 3-tuple for pickling a MaskedArray."""
+    return (_mareconstruct,
+            (a.__class__, (0,), 'b', ),
+            a.__getstate__())
+
+def dump(a,F):
+    """Pickles the MaskedArray `a` to the file `F`.
+`F` can either be the handle of an exiting file, or a string representing a file name.
+    """
+    if not hasattr(F,'readline'):
+        F = open(F,'w')
+    return cPickle.dump(a,F)
+
+def dumps(a):
+    """Returns a string corresponding to the pickling of the MaskedArray."""
+    return cPickle.dumps(a)
+
+def load(F):
+    """Wrapper around ``cPickle.load`` which accepts either a file-like object or
+ a filename."""
+    if not hasattr(F, 'readline'):
+        F = open(F,'r')
+    return cPickle.load(F)
+
+def loads(strg):
+    "Loads a pickle from the current string."""
+    return cPickle.loads(strg)
+
+MaskedArray.__getstate__ = _getstate
+MaskedArray.__setstate__ = _setstate
+MaskedArray.__reduce__ = _reduce
+MaskedArray.__dump__ = dump
+MaskedArray.__dumps__ = dumps
+
+################################################################################
+
+if __name__ == '__main__':
+    import numpy as N
+    if 1:
+        x = N.array([[ 0.13,  0.26,  0.90],
+                     [ 0.28,  0.33,  0.63],
+                     [ 0.31,  0.87,  0.70]])
+        m = N.array([[ True, False, False],
+                     [False, False, False],
+                     [True,  True, False]], dtype=N.bool_)
+        X = N.asmatrix(x)
+        mX = masked_array(X, mask=m)

Added: trunk/scipy/sandbox/maskedarray/alternative_versions/core_initial.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/alternative_versions/core_initial.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/alternative_versions/core_initial.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,2708 @@
+# pylint: disable-msg=E1002
+"""MA: a facility for dealing with missing observations
+MA is generally used as a numpy.array look-alike.
+by Paul F. Dubois.
+
+Copyright 1999, 2000, 2001 Regents of the University of California.
+Released for unlimited redistribution.
+Adapted for numpy_core 2005 by Travis Oliphant and
+(mainly) Paul Dubois.
+
+Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
+pgmdevlist_AT_gmail_DOT_com
+Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com)
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: core.py 254 2007-08-15 04:11:52Z backtopop $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: backtopop $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 254 $"
+__date__     = '$Date: 2007-08-15 00:11:52 -0400 (Wed, 15 Aug 2007) $'
+
+__all__ = ['MAError', 'MaskType', 'MaskedArray',
+           'bool_', 'complex_', 'float_', 'int_', 'object_',
+           'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
+               'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
+               'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
+               'arctanh', 'argmax', 'argmin', 'argsort', 'around',
+               'array', 'asarray',
+           'bitwise_and', 'bitwise_or', 'bitwise_xor',
+           'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
+               'cos', 'cosh', 'count',
+           'diagonal', 'divide', 'dump', 'dumps',
+           'empty', 'empty_like', 'equal', 'exp',
+           'fabs', 'fmod', 'filled', 'floor', 'floor_divide',
+           'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
+           'ids', 'inner', 'innerproduct',
+               'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
+           'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
+               'logical_and', 'logical_not', 'logical_or', 'logical_xor',
+           'make_mask', 'make_mask_none', 'mask_or', 'masked',
+               'masked_array', 'masked_equal', 'masked_greater',
+               'masked_greater_equal', 'masked_inside', 'masked_less',
+               'masked_less_equal', 'masked_not_equal', 'masked_object',
+               'masked_outside', 'masked_print_option', 'masked_singleton',
+               'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
+               'minimum', 'multiply',
+           'negative', 'nomask', 'nonzero', 'not_equal',
+           'ones', 'outer', 'outerproduct',
+           'power', 'product', 'ptp', 'put', 'putmask',
+           'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
+               'right_shift', 'round_',
+           'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
+               'subtract', 'sum', 'swapaxes',
+           'take', 'tan', 'tanh', 'transpose', 'true_divide',
+           'var', 'where',
+           'zeros']
+
+import sys
+import types
+import cPickle
+import operator
+#
+import numpy
+from numpy import bool_, complex_, float_, int_, object_, str_
+
+import numpy.core.umath as umath
+import numpy.core.fromnumeric  as fromnumeric
+import numpy.core.numeric as numeric
+import numpy.core.numerictypes as ntypes
+from numpy import bool_, dtype, typecodes, amax, amin, ndarray
+from numpy import expand_dims as n_expand_dims
+import warnings
+
+
+MaskType = bool_
+nomask = MaskType(0)
+
+divide_tolerance = 1.e-35
+numpy.seterr(all='ignore')
+
+# TODO: There's still a problem with N.add.reduce not working...
+# TODO: ...neither does N.add.accumulate
+
+#####--------------------------------------------------------------------------
+#---- --- Exceptions ---
+#####--------------------------------------------------------------------------
+class MAError(Exception):
+    "Class for MA related errors."
+    def __init__ (self, args=None):
+        "Creates an exception."
+        Exception.__init__(self,args)
+        self.args = args
+    def __str__(self):
+        "Calculates the string representation."
+        return str(self.args)
+    __repr__ = __str__
+
+#####--------------------------------------------------------------------------
+#---- --- Filling options ---
+#####--------------------------------------------------------------------------
+# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
+default_filler = {'b': True,
+                  'c' : 1.e20 + 0.0j,
+                  'f' : 1.e20,
+                  'i' : 999999,
+                  'O' : '?',
+                  'S' : 'N/A',
+                  'u' : 999999,
+                  'V' : '???',
+                  }
+max_filler = ntypes._minvals
+max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]])
+min_filler = ntypes._maxvals
+min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]])
+if 'float128' in ntypes.typeDict:
+    max_filler.update([(numpy.float128,-numeric.inf)])
+    min_filler.update([(numpy.float128, numeric.inf)])
+
+
+def default_fill_value(obj):
+    "Calculates the default fill value for an object `obj`."
+    if hasattr(obj,'dtype'):
+        defval = default_filler[obj.dtype.kind]
+    elif isinstance(obj, numeric.dtype):
+        defval = default_filler[obj.kind]
+    elif isinstance(obj, float):
+        defval = default_filler['f']
+    elif isinstance(obj, int) or isinstance(obj, long):
+        defval = default_filler['i']
+    elif isinstance(obj, str):
+        defval = default_filler['S']
+    elif isinstance(obj, complex):
+        defval = default_filler['c']
+    else:
+        defval = default_filler['O']
+    return defval
+
+def minimum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the minimum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = min_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return min_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return min_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return min_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return min_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def maximum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the maximum of `obj`."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = max_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return max_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return max_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return max_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return max_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def set_fill_value(a, fill_value):
+    "Sets the fill value of `a` if it is a masked array."
+    if isinstance(a, MaskedArray):
+        a.set_fill_value(fill_value)
+
+def get_fill_value(a):
+    """Returns the fill value of `a`, if any.
+    Otherwise, returns the default fill value for that type.
+    """
+    if isinstance(a, MaskedArray):
+        result = a.fill_value
+    else:
+        result = default_fill_value(a)
+    return result
+
+def common_fill_value(a, b):
+    "Returns the common fill_value of `a` and `b`, if any, or `None`."
+    t1 = get_fill_value(a)
+    t2 = get_fill_value(b)
+    if t1 == t2:
+        return t1
+    return None
+
+#................................................
+def filled(a, value = None):
+    """Returns `a` as an array with masked data replaced by `value`.
+If `value` is `None` or the special element `masked`, `get_fill_value(a)`
+is used instead.
+
+If `a` is already a contiguous numeric array, `a` itself is returned.
+
+`filled(a)` can be used to be sure that the result is numeric when passing
+an object a to other software ignorant of MA, in particular to numpy itself.
+    """
+    if hasattr(a, 'filled'):
+        return a.filled(value)
+    elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
+        return a
+    elif isinstance(a, dict):
+        return numeric.array(a, 'O')
+    else:
+        return numeric.array(a)
+
+def get_masked_subclass(*arrays):
+    """Returns the youngest subclass of MaskedArray from a list of arrays,
+ or MaskedArray. In case of siblings, the first takes over."""
+    if len(arrays) == 1:
+        arr = arrays[0]
+        if isinstance(arr, MaskedArray):
+            rcls = type(arr)
+        else:
+            rcls = MaskedArray
+    else:
+        arrcls = [type(a) for a in arrays]
+        rcls = arrcls[0]
+        if not issubclass(rcls, MaskedArray):
+            rcls = MaskedArray
+        for cls in arrcls[1:]:
+            if issubclass(cls, rcls):
+                rcls = cls
+    return rcls
+
+#####--------------------------------------------------------------------------
+#---- --- Ufuncs ---
+#####--------------------------------------------------------------------------
+ufunc_domain = {}
+ufunc_fills = {}
+
+class domain_check_interval:
+    """Defines a valid interval,
+so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`."""
+    def __init__(self, a, b):
+        "domain_check_interval(a,b)(x) = true where x < a or y > b"
+        if (a > b):
+            (a, b) = (b, a)
+        self.a = a
+        self.b = b
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.logical_or(umath.greater (x, self.b),
+                                umath.less(x, self.a))
+#............................
+class domain_tan:
+    """Defines a valid interval for the `tan` function,
+so that `domain_tan(eps) = True where `abs(cos(x)) < eps`"""
+    def __init__(self, eps):
+        "domain_tan(eps) = true where abs(cos(x)) < eps)"
+        self.eps = eps
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(umath.absolute(umath.cos(x)), self.eps)
+#............................
+class domain_safe_divide:
+    """defines a domain for safe division."""
+    def __init__ (self, tolerance=divide_tolerance):
+        self.tolerance = tolerance
+    def __call__ (self, a, b):
+        return umath.absolute(a) * self.tolerance >= umath.absolute(b)
+#............................
+class domain_greater:
+    "domain_greater(v)(x) = true where x <= v"
+    def __init__(self, critical_value):
+        "domain_greater(v)(x) = true where x <= v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less_equal(x, self.critical_value)
+#............................
+class domain_greater_equal:
+    "domain_greater_equal(v)(x) = true where x < v"
+    def __init__(self, critical_value):
+        "domain_greater_equal(v)(x) = true where x < v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Execute the call behavior."
+        return umath.less(x, self.critical_value)
+#..............................................................................
+class masked_unary_operation:
+    """Defines masked version of unary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fill` : Default filling value *[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mufunc, fill=0, domain=None):
+        """ masked_unary_operation(aufunc, fill=0, domain=None)
+            aufunc(fill) must be defined
+            self(x) returns aufunc(x)
+            with masked values where domain(x) is true or getmask(x) is true.
+        """
+        self.f = mufunc
+        self.fill = fill
+        self.domain = domain
+        self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
+        self.__name__ = getattr(mufunc, "__name__", str(mufunc))
+        ufunc_domain[mufunc] = domain
+        ufunc_fills[mufunc] = fill
+    #
+    def __call__ (self, a, *args, **kwargs):
+        "Execute the call behavior."
+# numeric tries to return scalars rather than arrays when given scalars.
+        m = getmask(a)
+        d1 = filled(a, self.fill)
+        if self.domain is not None:
+            m = mask_or(m, numeric.asarray(self.domain(d1)))
+        # Take care of the masked singletong first ...
+        if m.ndim == 0 and m:
+            return masked
+        # Get the result....
+        if isinstance(a, MaskedArray):
+            result = self.f(d1, *args, **kwargs).view(type(a))
+        else:
+            result = self.f(d1, *args, **kwargs).view(MaskedArray)
+        # Fix the mask if we don't have a scalar
+        if result.ndim > 0:
+            result._mask = m
+        return result
+    #
+    def __str__ (self):
+        return "Masked version of %s. [Invalid values are masked]" % str(self.f)
+#..............................................................................
+class masked_binary_operation:
+    """Defines masked version of binary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, mbfunc, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = mbfunc
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
+        self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
+        ufunc_domain[mbfunc] = None
+        ufunc_fills[mbfunc] = (fillx, filly)
+    #
+    def __call__ (self, a, b, *args, **kwargs):
+        "Execute the call behavior."
+        m = mask_or(getmask(a), getmask(b))
+        if (not m.ndim) and m:
+            return masked
+        d1 = filled(a, self.fillx)
+        d2 = filled(b, self.filly)
+# CHECK : Do we really need to fill the arguments ? Pro'ly not
+#        result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b))
+        result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b))
+        if result.ndim > 0:
+            result._mask = m
+        return result
+    #
+    def reduce (self, target, axis=0, dtype=None):
+        """Reduces `target` along the given `axis`."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = MaskedArray
+        m = getmask(target)
+        t = filled(target, self.filly)
+        if t.shape == ():
+            t = t.reshape(1)
+            if m is not nomask:
+                m = make_mask(m, copy=1)
+                m.shape = (1,)
+        if m is nomask:
+            return self.f.reduce(t, axis).view(tclass)
+        t = t.view(tclass)
+        t._mask = m
+        # XXX: "or t.dtype" below is a workaround for what appears
+        # XXX: to be a bug in reduce.
+        tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype)
+        mr = umath.logical_and.reduce(m, axis)
+        tr = tr.view(tclass)
+        if mr.ndim > 0:
+            tr._mask = mr
+            return tr
+        elif mr:
+            return masked
+        return tr
+
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = umath.logical_or.outer(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        rcls = get_masked_subclass(a,b)
+        d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
+        if d.ndim > 0:
+            d._mask = m
+        return d
+
+    def accumulate (self, target, axis=0):
+        """Accumulates `target` along `axis` after filling with y fill value."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = masked_array
+        t = filled(target, self.filly)
+        return self.f.accumulate(t, axis).view(tclass)
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+#..............................................................................
+class domained_binary_operation:
+    """Defines binary operations that have a domain, like divide.
+
+These are complicated so they are a separate class.
+They have no reduce, outer or accumulate.
+
+:IVariables:
+    - `f` : function.
+    - `fillx` : Default filling value for first array*[0]*.
+    - `filly` : Default filling value for second array*[0]*.
+    - `domain` : Default domain *[None]*.
+    """
+    def __init__ (self, dbfunc, domain, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = dbfunc
+        self.domain = domain
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
+        self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
+        ufunc_domain[dbfunc] = domain
+        ufunc_fills[dbfunc] = (fillx, filly)
+
+    def __call__(self, a, b):
+        "Execute the call behavior."
+        ma = getmask(a)
+        mb = getmask(b)
+        d1 = filled(a, self.fillx)
+        d2 = filled(b, self.filly)
+        t = numeric.asarray(self.domain(d1, d2))
+
+        if fromnumeric.sometrue(t, None):
+            d2 = numeric.where(t, self.filly, d2)
+            mb = mask_or(mb, t)
+        m = mask_or(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        result =  self.f(d1, d2).view(get_masked_subclass(a,b))
+        if result.ndim > 0:
+            result._mask = m
+        return result
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+
+#..............................................................................
+# Unary ufuncs
+exp = masked_unary_operation(umath.exp)
+conjugate = masked_unary_operation(umath.conjugate)
+sin = masked_unary_operation(umath.sin)
+cos = masked_unary_operation(umath.cos)
+tan = masked_unary_operation(umath.tan)
+arctan = masked_unary_operation(umath.arctan)
+arcsinh = masked_unary_operation(umath.arcsinh)
+sinh = masked_unary_operation(umath.sinh)
+cosh = masked_unary_operation(umath.cosh)
+tanh = masked_unary_operation(umath.tanh)
+abs = absolute = masked_unary_operation(umath.absolute)
+fabs = masked_unary_operation(umath.fabs)
+negative = masked_unary_operation(umath.negative)
+floor = masked_unary_operation(umath.floor)
+ceil = masked_unary_operation(umath.ceil)
+around = masked_unary_operation(fromnumeric.round_)
+logical_not = masked_unary_operation(umath.logical_not)
+# Domained unary ufuncs
+sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0))
+log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0))
+log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0))
+tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35))
+arcsin = masked_unary_operation(umath.arcsin, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccos = masked_unary_operation(umath.arccos, 0.0,
+                                domain_check_interval(-1.0, 1.0))
+arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0))
+arctanh = masked_unary_operation(umath.arctanh, 0.0,
+                                 domain_check_interval(-1.0+1e-15, 1.0-1e-15))
+# Binary ufuncs
+add = masked_binary_operation(umath.add)
+subtract = masked_binary_operation(umath.subtract)
+multiply = masked_binary_operation(umath.multiply, 1, 1)
+arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0)
+equal = masked_binary_operation(umath.equal)
+equal.reduce = None
+not_equal = masked_binary_operation(umath.not_equal)
+not_equal.reduce = None
+less_equal = masked_binary_operation(umath.less_equal)
+less_equal.reduce = None
+greater_equal = masked_binary_operation(umath.greater_equal)
+greater_equal.reduce = None
+less = masked_binary_operation(umath.less)
+less.reduce = None
+greater = masked_binary_operation(umath.greater)
+greater.reduce = None
+logical_and = masked_binary_operation(umath.logical_and)
+alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce
+logical_or = masked_binary_operation(umath.logical_or)
+sometrue = logical_or.reduce
+logical_xor = masked_binary_operation(umath.logical_xor)
+bitwise_and = masked_binary_operation(umath.bitwise_and)
+bitwise_or = masked_binary_operation(umath.bitwise_or)
+bitwise_xor = masked_binary_operation(umath.bitwise_xor)
+hypot = masked_binary_operation(umath.hypot)
+# Domained binary ufuncs
+divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1)
+true_divide = domained_binary_operation(umath.true_divide,
+                                        domain_safe_divide(), 0, 1)
+floor_divide = domained_binary_operation(umath.floor_divide,
+                                         domain_safe_divide(), 0, 1)
+remainder = domained_binary_operation(umath.remainder,
+                                      domain_safe_divide(), 0, 1)
+fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Mask creation functions ---
+#####--------------------------------------------------------------------------
+def getmask(a):
+    """Returns the mask of `a`, if any, or `nomask`.
+Returns `nomask` if `a` is not a masked array.
+To get an array for sure use getmaskarray."""
+    if hasattr(a, "_mask"):
+        return a._mask
+    else:
+        return nomask
+
+def getmaskarray(a):
+    """Returns the mask of `a`, if any.
+Otherwise, returns an array of `False`, with the same shape as `a`.
+    """
+    m = getmask(a)
+    if m is nomask:
+        return make_mask_none(fromnumeric.shape(a))
+    else:
+        return m
+
+def is_mask(m):
+    """Returns `True` if `m` is a legal mask.
+Does not check contents, only type.
+    """
+    try:
+        return m.dtype.type is MaskType
+    except AttributeError:
+        return False
+#
+def make_mask(m, copy=False, small_mask=True, flag=None):
+    """make_mask(m, copy=0, small_mask=0)
+Returns `m` as a mask, creating a copy if necessary or requested.
+The function can accept any sequence of integers or `nomask`.
+Does not check that contents must be 0s and 1s.
+If `small_mask=True`, returns `nomask` if `m` contains no true elements.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+    """
+    if flag is not None:
+        warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                      DeprecationWarning)
+        small_mask = flag
+    if m is nomask:
+        return nomask
+    elif isinstance(m, ndarray):
+        m = filled(m, True)
+        if m.dtype.type is MaskType:
+            if copy:
+                result = numeric.array(m, dtype=MaskType, copy=copy)
+            else:
+                result = m
+        else:
+            result = numeric.array(m, dtype=MaskType)
+    else:
+        result = numeric.array(filled(m, True), dtype=MaskType)
+    # Bas les masques !
+    if small_mask and not result.any():
+        return nomask
+    else:
+        return result
+
+def make_mask_none(s):
+    "Returns a mask of shape `s`, filled with `False`."
+    result = numeric.zeros(s, dtype=MaskType)
+    return result
+
+def mask_or (m1, m2, copy=False, small_mask=True):
+    """Returns the combination of two masks `m1` and `m2`.
+The masks are combined with the `logical_or` operator, treating `nomask` as false.
+The result may equal m1 or m2 if the other is nomask.
+
+:Parameters:
+    - `m` (ndarray) : Mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+     """
+    if m1 is nomask:
+        return make_mask(m2, copy=copy, small_mask=small_mask)
+    if m2 is nomask:
+        return make_mask(m1, copy=copy, small_mask=small_mask)
+    if m1 is m2 and is_mask(m1):
+        return m1
+    return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
+
+#####--------------------------------------------------------------------------
+#--- --- Masking functions ---
+#####--------------------------------------------------------------------------
+def masked_where(condition, a, copy=True):
+    """Returns `x` as an array masked where `condition` is true.
+Masked values of `x` or `condition` are kept.
+
+:Parameters:
+    - `condition` (ndarray) : Masking condition.
+    - `x` (ndarray) : Array to mask.
+    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+    """
+    cond = filled(condition,1)
+    a = numeric.array(a, copy=copy, subok=True)
+    if hasattr(a, '_mask'):
+        cond = mask_or(cond, a._mask)
+        cls = type(a)
+    else:
+        cls = MaskedArray
+    result = a.view(cls)
+    result._mask = cond
+    return result
+
+def masked_greater(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x > value)``."
+    return masked_where(greater(x, value), x, copy=copy)
+
+def masked_greater_equal(x, value, copy=1):
+    "Shortcut to `masked_where`, with ``condition = (x >= value)``."
+    return masked_where(greater_equal(x, value), x, copy=copy)
+
+def masked_less(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x < value)``."
+    return masked_where(less(x, value), x, copy=copy)
+
+def masked_less_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x <= value)``."
+    return masked_where(less_equal(x, value), x, copy=copy)
+
+def masked_not_equal(x, value, copy=True):
+    "Shortcut to `masked_where`, with ``condition = (x != value)``."
+    return masked_where((x != value), x, copy=copy)
+
+#
+def masked_equal(x, value, copy=True):
+    """Shortcut to `masked_where`, with ``condition = (x == value)``.
+For floating point, consider `masked_values(x, value)` instead.
+    """
+    return masked_where((x == value), x, copy=copy)
+#    d = filled(x, 0)
+#    c = umath.equal(d, value)
+#    m = mask_or(c, getmask(x))
+#    return array(d, mask=m, copy=copy)
+
+def masked_inside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x inside
+the interval `[v1,v2]` ``(v1 <= x <= v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf >= v1) & (xf <= v2)
+    return masked_where(condition, x, copy=copy)
+
+def masked_outside(x, v1, v2, copy=True):
+    """Shortcut to `masked_where`, where `condition` is True for x outside
+the interval `[v1,v2]` ``(x < v1)|(x > v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf < v1) | (xf > v2)
+    return masked_where(condition, x, copy=copy)
+
+#
+def masked_object(x, value, copy=True):
+    """Masks the array `x` where the data are exactly equal to `value`.
+This function is suitable only for `object` arrays: for floating point,
+please use `masked_values` instead.
+The mask is set to `nomask` if posible.
+
+:parameter copy (Boolean, *[True]*):  Returns a copy of `x` if true. """
+    if isMaskedArray(x):
+        condition = umath.equal(x._data, value)
+        mask = x._mask
+    else:
+        condition = umath.equal(fromnumeric.asarray(x), value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(x, mask=mask, copy=copy, fill_value=value)
+
+def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
+    """Masks the array `x` where the data are approximately equal to `value`
+(that is, ``abs(x - value) <= atol+rtol*abs(value)``).
+Suitable only for floating points. For integers, please use `masked_equal`.
+The mask is set to `nomask` if posible.
+
+:Parameters:
+    - `rtol` (Float, *[1e-5]*): Tolerance parameter.
+    - `atol` (Float, *[1e-8]*): Tolerance parameter.
+    - `copy` (boolean, *[False]*) : Returns a copy of `x` if True.
+    """
+    abs = umath.absolute
+    xnew = filled(x, value)
+    if issubclass(xnew.dtype.type, numeric.floating):
+        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+        try:
+            mask = x._mask
+        except AttributeError:
+            mask = nomask
+    else:
+        condition = umath.equal(xnew, value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, small_mask=True))
+    return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
+
+#####--------------------------------------------------------------------------
+#---- --- Printing options ---
+#####--------------------------------------------------------------------------
+class _MaskedPrintOption:
+    """Handles the string used to represent missing data in a masked array."""
+    def __init__ (self, display):
+        "Creates the masked_print_option object."
+        self._display = display
+        self._enabled = True
+
+    def display(self):
+        "Displays the string to print for masked values."
+        return self._display
+
+    def set_display (self, s):
+        "Sets the string to print for masked values."
+        self._display = s
+
+    def enabled(self):
+        "Is the use of the display value enabled?"
+        return self._enabled
+
+    def enable(self, small_mask=1):
+        "Set the enabling small_mask to `small_mask`."
+        self._enabled = small_mask
+
+    def __str__ (self):
+        return str(self._display)
+
+    __repr__ = __str__
+
+#if you single index into a masked location you get this object.
+masked_print_option = _MaskedPrintOption('--')
+
+#####--------------------------------------------------------------------------
+#---- --- MaskedArray class ---
+#####--------------------------------------------------------------------------
+##def _getoptions(a_out, a_in):
+##    "Copies standards options of a_in to a_out."
+##    for att in [']
+#class _mathmethod(object):
+#    """Defines a wrapper for arithmetic methods.
+#Instead of directly calling a ufunc, the corresponding method of  the `array._data`
+#object is called instead.
+#    """
+#    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+#        """
+#:Parameters:
+#    - `methodname` (String) : Method name.
+#    - `fill_self` (Float *[0]*) : Fill value for the instance.
+#    - `fill_other` (Float *[0]*) : Fill value for the target.
+#    - `domain` (Domain object *[None]*) : Domain of non-validity.
+#        """
+#        self.methodname = methodname
+#        self.fill_self = fill_self
+#        self.fill_other = fill_other
+#        self.domain = domain
+#        self.obj = None
+#        self.__doc__ = self.getdoc()
+#    #
+#    def getdoc(self):
+#        "Returns the doc of the function (from the doc of the method)."
+#        try:
+#            return getattr(MaskedArray, self.methodname).__doc__
+#        except:
+#            return getattr(ndarray, self.methodname).__doc__
+#    #
+#    def __get__(self, obj, objtype=None):
+#        self.obj = obj
+#        return self
+#    #
+#    def __call__ (self, other, *args):
+#        "Execute the call behavior."
+#        instance = self.obj
+#        m_self = instance._mask
+#        m_other = getmask(other)
+#        base = instance.filled(self.fill_self)
+#        target = filled(other, self.fill_other)
+#        if self.domain is not None:
+#            # We need to force the domain to a ndarray only.
+#            if self.fill_other > self.fill_self:
+#                domain = self.domain(base, target)
+#            else:
+#                domain = self.domain(target, base)
+#            if domain.any():
+#                #If `other` is a subclass of ndarray, `filled` must have the
+#                # same subclass, else we'll lose some info.
+#                #The easiest then is to fill `target` instead of creating
+#                # a pure ndarray.
+#                #Oh, and we better make a copy!
+#                if isinstance(other, ndarray):
+#                    # We don't want to modify other: let's copy target, then
+#                    target = target.copy()
+#                    target[fromnumeric.asarray(domain)] = self.fill_other
+#                else:
+#                    target = numeric.where(fromnumeric.asarray(domain),
+#                                           self.fill_other, target)
+#                m_other = mask_or(m_other, domain)
+#        m = mask_or(m_self, m_other)
+#        method = getattr(base, self.methodname)
+#        result = method(target, *args).view(type(instance))
+#        try:
+#            result._mask = m
+#        except AttributeError:
+#            if m:
+#                result = masked
+#        return result
+#...............................................................................
+class _arraymethod(object):
+    """Defines a wrapper for basic array methods.
+Upon call, returns a masked array, where the new `_data` array is the output
+of the corresponding method called on the original `_data`.
+
+If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+If `onmask` is False, the new mask is just a reference to the initial mask.
+
+:Parameters:
+    `funcname` : String
+        Name of the function to apply on data.
+    `onmask` : Boolean *[True]*
+        Whether the mask must be processed also (True) or left alone (False).
+    """
+    def __init__(self, funcname, onmask=True):
+        self._name = funcname
+        self._onmask = onmask
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        methdoc = getattr(ndarray, self._name, None)
+        methdoc = getattr(numpy, self._name, methdoc)
+#        methdoc = getattr(MaskedArray, self._name, methdoc)
+        if methdoc is not None:
+            return methdoc.__doc__
+#        try:
+#            return getattr(MaskedArray, self._name).__doc__
+#        except:
+#            try:
+#                return getattr(numpy, self._name).__doc__
+#            except:
+#                return getattr(ndarray, self._name).__doc
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__(self, *args, **params):
+        methodname = self._name
+        data = self.obj._data
+        mask = self.obj._mask
+        cls = type(self.obj)
+        result = getattr(data, methodname)(*args, **params).view(cls)
+        result._smallmask = self.obj._smallmask
+        if result.ndim:
+            if not self._onmask:
+                result._mask = mask
+            elif mask is not nomask:
+                result.__setmask__(getattr(mask, methodname)(*args, **params))
+        return result
+#..........................................................
+
+class flatiter(object):
+    "Defines an interator."
+    def __init__(self, ma):
+        self.ma = ma
+        self.ma_iter = numpy.asarray(ma).flat
+
+        if ma._mask is nomask:
+            self.maskiter = None
+        else:
+            self.maskiter = ma._mask.flat
+
+    def __iter__(self):
+        return self
+
+    ### This won't work is ravel makes a copy
+    def __setitem__(self, index, value):
+        a = self.ma.ravel()
+        a[index] = value
+
+    def next(self):
+        d = self.ma_iter.next()
+        if self.maskiter is not None and self.maskiter.next():
+            d = masked
+        return d
+
+
+class MaskedArray(numeric.ndarray):
+    """Arrays with possibly masked values.
+Masked values of True exclude the corresponding element from any computation.
+
+Construction:
+    x = array(data, dtype=None, copy=True, order=False,
+              mask = nomask, fill_value=None, small_mask=True)
+
+If copy=False, every effort is made not to copy the data:
+If `data` is a MaskedArray, and argument mask=nomask, then the candidate data
+is `data._data` and the mask used is `data._mask`.
+If `data` is a numeric array, it is used as the candidate raw data.
+If `dtype` is not None and is different from data.dtype.char then a data copy is required.
+Otherwise, the candidate is used.
+
+If a data copy is required, the raw (unmasked) data stored is the result of:
+numeric.array(data, dtype=dtype.char, copy=copy)
+
+If `mask` is `nomask` there are no masked values.
+Otherwise mask must be convertible to an array of booleans with the same shape as x.
+If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`.
+Otherwise, the mask is not compressed.
+
+fill_value is used to fill in masked values when necessary, such as when
+printing and in method/function filled().
+The fill_value is not used for computation within this module.
+    """
+    __array_priority__ = 10.1
+    _defaultmask = nomask
+    _defaulthardmask = False
+    _baseclass =  numeric.ndarray
+    def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
+                keep_mask=True, small_mask=True, hard_mask=False, flag=None,
+                subok=True, **options):
+        """array(data, dtype=None, copy=True, mask=nomask, fill_value=None)
+
+If `data` is already a ndarray, its dtype becomes the default value of dtype.
+        """
+        if flag is not None:
+            warnings.warn("The flag 'flag' is now called 'small_mask'!",
+                          DeprecationWarning)
+            small_mask = flag
+        # Process data............
+        _data = numeric.array(data, dtype=dtype, copy=copy, subok=subok)
+        _baseclass = getattr(data, '_baseclass', type(_data))
+        _basedict = getattr(data, '_basedict', getattr(data, '__dict__', None))
+        if not isinstance(data, MaskedArray):
+            _data = _data.view(cls)
+        elif not subok:
+            _data = data.view(cls)
+        else:
+            _data = _data.view(type(data))
+        # Backwards compat .......
+        if hasattr(data,'_mask') and not isinstance(data, ndarray):
+            _data._mask = data._mask
+            _sharedmask = True
+        # Process mask ...........
+        if mask is nomask:
+            if not keep_mask:
+                _data._mask = nomask
+            if copy:
+                _data._mask = _data._mask.copy()
+        else:
+            mask = numeric.array(mask, dtype=MaskType, copy=copy)
+            if mask.shape != _data.shape:
+                (nd, nm) = (_data.size, mask.size)
+                if nm == 1:
+                    mask = numeric.resize(mask, _data.shape)
+                elif nm == nd:
+                    mask = fromnumeric.reshape(mask, _data.shape)
+                else:
+                    msg = "Mask and data not compatible: data size is %i, "+\
+                          "mask size is %i."
+                    raise MAError, msg % (nd, nm)
+            if _data._mask is nomask:
+                _data._mask = mask
+                _data._sharedmask = True
+            else:
+                # Make a copy of the mask to avoid propagation
+                _data._sharedmask = False
+                if not keep_mask:
+                    _data._mask = mask
+                else:
+                    _data._mask = umath.logical_or(mask, _data._mask)
+
+
+        # Update fill_value.......
+        _data._fill_value = getattr(data, '_fill_value', fill_value)
+        if _data._fill_value is None:
+            _data._fill_value = default_fill_value(_data)
+        # Process extra options ..
+        _data._hardmask = hard_mask
+        _data._smallmask = small_mask
+        _data._baseclass = _baseclass
+        _data._basedict = _basedict
+        return _data
+    #........................
+    def __array_finalize__(self,obj):
+        """Finalizes the masked array.
+        """
+        # Finalize mask ...............
+        self._mask = getattr(obj, '_mask', nomask)
+        if self._mask is not nomask:
+            self._mask.shape = self.shape
+        # Get the remaining options ...
+        self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask)
+        self._smallmask = getattr(obj, '_smallmask', True)
+        self._sharedmask = True
+        self._baseclass = getattr(obj, '_baseclass', type(obj))
+        self._fill_value = getattr(obj, '_fill_value', None)
+        # Update special attributes ...
+        self._basedict = getattr(obj, '_basedict', getattr(obj, '__dict__', None))
+        if self._basedict is not None:
+            self.__dict__.update(self._basedict)
+        return
+    #..................................
+    def __array_wrap__(self, obj, context=None):
+        """Special hook for ufuncs.
+Wraps the numpy array and sets the mask according to context.
+        """
+        #TODO : Should we check for type result
+        result = obj.view(type(self))
+        #..........
+        if context is not None:
+            result._mask = result._mask.copy()
+            (func, args, _) = context
+            m = reduce(mask_or, [getmask(arg) for arg in args])
+            # Get domain mask
+            domain = ufunc_domain.get(func, None)
+            if domain is not None:
+                if len(args) > 2:
+                    d = reduce(domain, args)
+                else:
+                    d = domain(*args)
+                if m is nomask:
+                    if d is not nomask:
+                        m = d
+                else:
+                    m |= d
+            if not m.ndim and m:
+                if m:
+                    if result.shape == ():
+                        return masked
+                    result._mask = numeric.ones(result.shape, bool_)
+            else:
+                result._mask = m
+        #....
+#        result._mask = m
+        result._fill_value = self._fill_value
+        result._hardmask = self._hardmask
+        result._smallmask = self._smallmask
+        result._baseclass = self._baseclass
+        return result
+    #.............................................
+    def __getitem__(self, indx):
+        """x.__getitem__(y) <==> x[y]
+Returns the item described by i. Not a copy as in previous versions.
+        """
+        # This test is useful, but we should keep things light...
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        # super() can't work here if the underlying data is a matrix...
+        dout = (self._data).__getitem__(indx)
+        m = self._mask
+        if hasattr(dout, 'shape') and len(dout.shape) > 0:
+            # Not a scalar: make sure that dout is a MA
+            dout = dout.view(type(self))
+            dout._smallmask = self._smallmask
+            if m is not nomask:
+                # use _set_mask to take care of the shape
+                dout.__setmask__(m[indx])
+        elif m is not nomask and m[indx]:
+            return masked
+        return dout
+    #........................
+    def __setitem__(self, indx, value):
+        """x.__setitem__(i, y) <==> x[i]=y
+Sets item described by index. If value is masked, masks those locations.
+        """
+        if self is masked:
+            raise MAError, 'Cannot alter the masked element.'
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        #....
+        if value is masked:
+            m = self._mask
+            if m is nomask:
+                m = make_mask_none(self.shape)
+#            else:
+#                m = m.copy()
+            m[indx] = True
+            self.__setmask__(m)
+            return
+        #....
+        dval = numeric.asarray(value).astype(self.dtype)
+        valmask = getmask(value)
+        if self._mask is nomask:
+            if valmask is not nomask:
+                self._mask = make_mask_none(self.shape)
+                self._mask[indx] = valmask
+        elif not self._hardmask:
+            _mask = self._mask.copy()
+            if valmask is nomask:
+                _mask[indx] = False
+            else:
+                _mask[indx] = valmask
+            self._set_mask(_mask)
+        elif hasattr(indx, 'dtype') and (indx.dtype==bool_):
+            indx = indx * umath.logical_not(self._mask)
+        else:
+            mindx = mask_or(self._mask[indx], valmask, copy=True)
+            dindx = self._data[indx]
+            if dindx.size > 1:
+                dindx[~mindx] = dval
+            elif mindx is nomask:
+                dindx = dval
+            dval = dindx
+            self._mask[indx] = mindx
+        # Set data ..........
+        #dval = filled(value).astype(self.dtype)
+        ndarray.__setitem__(self._data,indx,dval)
+    #............................................
+    def __getslice__(self, i, j):
+        """x.__getslice__(i, j) <==> x[i:j]
+Returns the slice described by i, j.
+The use of negative indices is not supported."""
+        return self.__getitem__(slice(i,j))
+    #........................
+    def __setslice__(self, i, j, value):
+        """x.__setslice__(i, j, value) <==> x[i:j]=value
+Sets a slice i:j to `value`.
+If `value` is masked, masks those locations."""
+        self.__setitem__(slice(i,j), value)
+    #............................................
+    def __setmask__(self, mask, copy=False):
+        newmask = make_mask(mask, copy=copy, small_mask=self._smallmask)
+#        self.unshare_mask()
+        if self._mask is nomask:
+            self._mask = newmask
+        elif self._hardmask:
+            if newmask is not nomask:
+                self._mask.__ior__(newmask)
+        else:
+            # This one is tricky: if we set the mask that way, we may break the
+            # propagation. But if we don't, we end up with a mask full of False
+            # and a test on nomask fails...
+            if newmask is nomask:
+                self._mask = nomask
+            else:
+                self._mask.flat = newmask
+        if self._mask.shape:
+            self._mask = numeric.reshape(self._mask, self.shape)
+    _set_mask = __setmask__
+
+    def _get_mask(self):
+        """Returns the current mask."""
+        return self._mask
+
+    mask = property(fget=_get_mask, fset=__setmask__, doc="Mask")
+    #............................................
+    def harden_mask(self):
+        "Forces the mask to hard."
+        self._hardmask = True
+
+    def soften_mask(self):
+        "Forces the mask to soft."
+        self._hardmask = False
+
+    def unshare_mask(self):
+        "Copies the mask and set the sharedmask flag to False."
+        if self._sharedmask:
+            self._mask = self._mask.copy()
+            self._sharedmask = False
+
+    #............................................
+    def _get_data(self):
+        "Returns the current data (as a view of the original underlying data)>"
+        return self.view(self._baseclass)
+    _data = property(fget=_get_data)
+    #............................................
+    def _get_flat(self):
+        """Calculates the flat value.
+        """
+        return flatiter(self)
+    #
+    def _set_flat (self, value):
+        "x.flat = value"
+        y = self.ravel()
+        y[:] = value
+    #
+    flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version")
+    #............................................
+    def get_fill_value(self):
+        "Returns the filling value."
+        if self._fill_value is None:
+            self._fill_value = default_fill_value(self)
+        return self._fill_value
+
+    def set_fill_value(self, value=None):
+        """Sets the filling value to `value`.
+If None, uses the default, based on the data type."""
+        if value is None:
+            value = default_fill_value(self)
+        self._fill_value = value
+
+    fill_value = property(fget=get_fill_value, fset=set_fill_value,
+                          doc="Filling value")
+
+    def filled(self, fill_value=None):
+        """Returns an array of the same class as `_data`,
+ with masked values filled with `fill_value`.
+Subclassing is preserved.
+
+If `fill_value` is None, uses self.fill_value.
+        """
+        m = self._mask
+        if m is nomask or not m.any():
+            return self._data
+        #
+        if fill_value is None:
+            fill_value = self.fill_value
+        #
+        if self is masked_singleton:
+            result = numeric.asanyarray(fill_value)
+        else:
+            result = self._data.copy()
+            try:
+                numpy.putmask(result, m, fill_value)
+                #result[m] = fill_value
+            except (TypeError, AttributeError):
+                fill_value = numeric.array(fill_value, dtype=object)
+                d = result.astype(object)
+                result = fromnumeric.choose(m, (d, fill_value))
+            except IndexError:
+                #ok, if scalar
+                if self._data.shape:
+                    raise
+                elif m:
+                    result = numeric.array(fill_value, dtype=self.dtype)
+                else:
+                    result = self._data
+        return result
+
+    def compressed(self):
+        "A 1-D array of all the non-masked data."
+        d = self.ravel()
+        if self._mask is nomask:
+            return d
+        elif not self._smallmask and not self._mask.any():
+            return d
+        else:
+            return d[numeric.logical_not(d._mask)]
+    #............................................
+    def __str__(self):
+        """x.__str__() <==> str(x)
+Calculates the string representation, using masked for fill if it is enabled.
+Otherwise, fills with fill value.
+        """
+        if masked_print_option.enabled():
+            f = masked_print_option
+            if self is masked:
+                return str(f)
+            m = self._mask
+            if m is nomask:
+                res = self._data
+            else:
+                if m.shape == ():
+                    if m:
+                        return str(f)
+                    else:
+                        return str(self._data)
+                # convert to object array to make filled work
+#CHECK: the two lines below seem more robust than the self._data.astype
+#                res = numeric.empty(self._data.shape, object_)
+#                numeric.putmask(res,~m,self._data)
+                res = self._data.astype("|O8")
+                res[m] = f
+        else:
+            res = self.filled(self.fill_value)
+        return str(res)
+
+    def __repr__(self):
+        """x.__repr__() <==> repr(x)
+Calculates the repr representation, using masked for fill if it is enabled.
+Otherwise fill with fill value.
+        """
+        with_mask = """\
+masked_%(name)s(data =
+ %(data)s,
+      mask =
+ %(mask)s,
+      fill_value=%(fill)s)
+"""
+        with_mask1 = """\
+masked_%(name)s(data = %(data)s,
+      mask = %(mask)s,
+      fill_value=%(fill)s)
+"""
+        n = len(self.shape)
+        name = repr(self._data).split('(')[0]
+        if n <= 1:
+            return with_mask1 % {
+                'name': name,
+                'data': str(self),
+                'mask': str(self._mask),
+                'fill': str(self.fill_value),
+                }
+        return with_mask % {
+            'name': name,
+            'data': str(self),
+            'mask': str(self._mask),
+            'fill': str(self.fill_value),
+            }
+    #............................................
+    def __iadd__(self, other):
+        "Adds other to self in place."
+        ndarray.__iadd__(self._data,other)
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __isub__(self, other):
+        "Subtracts other from self in place."
+        ndarray.__isub__(self._data,other)
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __imul__(self, other):
+        "Multiplies self by other in place."
+        ndarray.__imul__(self._data,other)
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __idiv__(self, other):
+        "Divides self by other in place."
+        dom_mask = domain_safe_divide().__call__(self, filled(other,1))
+        other_mask = getmask(other)
+        new_mask = mask_or(other_mask, dom_mask)
+        ndarray.__idiv__(self._data, other)
+        self._mask = mask_or(self._mask, new_mask)
+        return self
+    #............................................
+    def __float__(self):
+        "Converts self to float."
+        if self._mask is not nomask:
+            warnings.warn("Warning: converting a masked element to nan.")
+            return numpy.nan
+            #raise MAError, 'Cannot convert masked element to a Python float.'
+        return float(self.item())
+
+    def __int__(self):
+        "Converts self to int."
+        if self._mask is not nomask:
+            raise MAError, 'Cannot convert masked element to a Python int.'
+        return int(self.item())
+    #............................................
+    def count(self, axis=None):
+        """Counts the non-masked elements of the array along a given axis,
+and returns a masked array where the mask is True where all data are masked.
+If `axis` is None, counts all the non-masked elements, and returns either a
+scalar or the masked singleton."""
+        m = self._mask
+        s = self.shape
+        ls = len(s)
+        if m is nomask:
+            if ls == 0:
+                return 1
+            if ls == 1:
+                return s[0]
+            if axis is None:
+                return self.size
+            else:
+                n = s[axis]
+                t = list(s)
+                del t[axis]
+                return numeric.ones(t) * n
+        n1 = fromnumeric.size(m, axis)
+        n2 = m.astype(int_).sum(axis)
+        if axis is None:
+            return (n1-n2)
+        else:
+            return masked_array(n1 - n2)
+    #............................................
+    def reshape (self, *s):
+        """Reshapes the array to shape s.
+Returns a new masked array.
+If you want to modify the shape in place, please use `a.shape = s`"""
+        result = self._data.reshape(*s).view(type(self))
+        result.__dict__.update(self.__dict__)
+        if result._mask is not nomask:
+            result._mask = self._mask.copy()
+            result._mask.shape = result.shape
+        return result
+    #
+    repeat = _arraymethod('repeat')
+    #
+    def resize(self, newshape, refcheck=True, order=False):
+        """Attempts to modify size and shape of self inplace.
+        The array must own its own memory and not be referenced by other arrays.
+        Returns None.
+        """
+        try:
+            self._data.resize(newshape, refcheck, order)
+            if self.mask is not nomask:
+                self._mask.resize(newshape, refcheck, order)
+        except ValueError:
+            raise ValueError("Cannot resize an array that has been referenced "
+                             "or is referencing another array in this way.\n"
+                             "Use the resize function.")
+        return None
+    #
+    flatten = _arraymethod('flatten')
+    #
+    def put(self, indices, values, mode='raise'):
+        """Sets storage-indexed locations to corresponding values.
+a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
+`values` can be scalar or an array shorter than indices, and it will be repeated,
+if necessary.
+If `values` has some masked values, the initial mask is updated in consequence,
+else the corresponding values are unmasked.
+        """
+        m = self._mask
+        # Hard mask: Get rid of the values/indices that fall on masked data
+        if self._hardmask and self._mask is not nomask:
+            mask = self._mask[indices]
+            indices = numeric.asarray(indices)
+            values = numeric.asanyarray(values)
+            values.resize(indices.shape)
+            indices = indices[~mask]
+            values = values[~mask]
+        #....
+        self._data.put(indices, values, mode=mode)
+        #....
+        if m is nomask:
+            m = getmask(values)
+        else:
+            m = m.copy()
+            if getmask(values) is nomask:
+                m.put(indices, False, mode=mode)
+            else:
+                m.put(indices, values._mask, mode=mode)
+            m = make_mask(m, copy=False, small_mask=True)
+        self._mask = m
+    #............................................
+    def ids (self):
+        """Return the address of the data and mask areas."""
+        return (self.ctypes.data, self._mask.ctypes.data)
+    #............................................
+    def all(self, axis=None, out=None):
+        """a.all(axis) returns True if all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as True during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+    Note: the out argument is not really operational...
+        """
+        d = self.filled(True).all(axis=axis, out=out).view(type(self))
+        if d.ndim > 0:
+            d.__setmask__(self._mask.all(axis))
+        return d
+
+    def any(self, axis=None, out=None):
+        """a.any(axis) returns True if some or all entries along the axis are True.
+    Returns False otherwise. If axis is None, uses the flatten array.
+    Masked data are considered as False during computation.
+    Outputs a masked array, where the mask is True if all data are masked along the axis.
+    Note: the out argument is not really operational...
+        """
+        d = self.filled(False).any(axis=axis, out=out).view(type(self))
+        if d.ndim > 0:
+            d.__setmask__(self._mask.all(axis))
+        return d
+
+    def nonzero(self):
+        """a.nonzero() returns a tuple of arrays
+
+    Returns a tuple of arrays, one for each dimension of a,
+    containing the indices of the non-zero elements in that
+    dimension.  The corresponding non-zero values can be obtained
+    with
+        a[a.nonzero()].
+
+    To group the indices by element, rather than dimension, use
+        transpose(a.nonzero())
+    instead. The result of this is always a 2d array, with a row for
+    each non-zero element."""
+        return numeric.asarray(self.filled(0)).nonzero()
+    #............................................
+    def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
+        """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
+Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
+        """
+        # TODO: What are we doing with `out`?
+        m = self._mask
+        if m is nomask:
+            result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
+                                                    axis2=axis2, out=out)
+            return result.astype(dtype)
+        else:
+            D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
+            return D.astype(dtype).sum(axis=None)
+    #............................................
+    def sum(self, axis=None, dtype=None):
+        """a.sum(axis=None, dtype=None)
+Sums the array `a` over the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+    """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
+        if result.ndim > 0:
+            result.__setmask__(mask)
+        return result
+
+    def cumsum(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative sum of the elements of array `a` along the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def prod(self, axis=None, dtype=None):
+        """a.prod(axis=None, dtype=None)
+Returns the product of the elements of array `a` along the given axis `axis`.
+Masked elements are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
+        if result.ndim:
+            result.__setmask__(mask)
+        return result
+    product = prod
+
+    def cumprod(self, axis=None, dtype=None):
+        """a.cumprod(axis=None, dtype=None)
+Returns the cumulative product of ethe lements of array `a` along the given axis `axis`.
+Masked values are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+        """
+        result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def mean(self, axis=None, dtype=None):
+        """a.mean(axis=None, dtype=None)
+
+    Averages the array over the given axis.  If the axis is None,
+    averages over all dimensions of the array.  Equivalent to
+
+      a.sum(axis, dtype) / size(a, axis).
+
+    The optional dtype argument is the data type for intermediate
+    calculations in the sum.
+
+    Returns a masked array, of the same class as a.
+        """
+        if self._mask is nomask:
+            return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
+        else:
+            dsum = self.sum(axis=axis, dtype=dtype)
+            cnt = self.count(axis=axis)
+            return dsum*1./cnt
+
+    def anom(self, axis=None, dtype=None):
+        """a.anom(axis=None, dtype=None)
+    Returns the anomalies, or deviation from the average.
+            """
+        m = self.mean(axis, dtype)
+        if not axis:
+            return (self - m)
+        else:
+            return (self - expand_dims(m,axis))
+
+    def var(self, axis=None, dtype=None):
+        """a.var(axis=None, dtype=None)
+Returns the variance, a measure of the spread of a distribution.
+
+The variance is the average of the squared deviations from the mean,
+i.e. var = mean((x - x.mean())**2).
+        """
+        if self._mask is nomask:
+            # TODO: Do we keep super, or var _data and take a view ?
+            return super(MaskedArray, self).var(axis=axis, dtype=dtype)
+        else:
+            cnt = self.count(axis=axis)
+            danom = self.anom(axis=axis, dtype=dtype)
+            danom *= danom
+            dvar = numeric.array(danom.sum(axis) / cnt).view(type(self))
+            if axis is not None:
+                dvar._mask = mask_or(self._mask.all(axis), (cnt==1))
+            return dvar
+
+    def std(self, axis=None, dtype=None):
+        """a.std(axis=None, dtype=None)
+Returns the standard deviation, a measure of the spread of a distribution.
+
+The standard deviation is the square root of the average of the squared
+deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
+        """
+        dvar = self.var(axis,dtype)
+        if axis is not None or dvar is not masked:
+            dvar = sqrt(dvar)
+        return dvar
+    #............................................
+    def argsort(self, axis=None, fill_value=None, kind='quicksort',
+                order=None):
+        """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argsort(axis=axis, kind=kind, order=order)
+    #........................
+    def argmin(self, axis=None, fill_value=None):
+        """Returns a ndarray of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the minimum default for the data type.
+        """
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argmin(axis)
+    #........................
+    def argmax(self, axis=None, fill_value=None):
+        """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the maximum default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+        """
+        if fill_value is None:
+            fill_value = maximum_fill_value(self._data)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argmax(axis)
+
+    def sort(self, axis=-1, kind='quicksort', order=None,
+             endwith=True, fill_value=None):
+        """
+        Sort a along the given axis.
+
+    Keyword arguments:
+
+    axis  -- axis to be sorted (default -1)
+    kind  -- sorting algorithm (default 'quicksort')
+             Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+    order -- If a has fields defined, then the order keyword can be the
+             field name to sort on or a list (or tuple) of field names
+             to indicate the order that fields should be used to define
+             the sort.
+    endwith--Boolean flag indicating whether missing values (if any) should
+             be forced in the upper indices (at the end of the array) or
+             lower indices (at the beginning).
+
+    Returns: None.
+
+    This method sorts 'a' in place along the given axis using the algorithm
+    specified by the kind keyword.
+
+    The various sorts may characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order and is most
+    useful when used with argsort where the key might differ from the items
+    being sorted. The three available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    """
+        if self._mask is nomask:
+            ndarray.sort(self,axis=axis, kind=kind, order=order)
+        else:
+            if fill_value is None:
+                if endwith:
+                    filler = minimum_fill_value(self)
+                else:
+                    filler = maximum_fill_value(self)
+            else:
+                filler = fill_value
+            idx = numpy.indices(self.shape)
+            idx[axis] = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
+            idx_l = idx.tolist()
+            tmp_mask = self._mask[idx_l].flat
+            tmp_data = self._data[idx_l].flat
+            self.flat = tmp_data
+            self._mask.flat = tmp_mask
+        return
+    #............................................
+    def min(self, axis=None, fill_value=None):
+        """Returns the minimum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).min(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Get the mask ................
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fil value ...........
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).min(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def max(self, axis=None, fill_value=None):
+        """Returns the maximum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).max(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Check the mask ..............
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fill value ..........
+        if fill_value is None:
+            fill_value = maximum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).max(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def ptp(self, axis=None, fill_value=None):
+        """Returns the visible data range (max-min) along the given axis.
+If the axis is `None`, applies on a flattened array. Masked values are filled
+with `fill_value` for processing. If `fill_value` is None, the maximum is uses
+the maximum default, the minimum uses the minimum default."""
+        return self.max(axis, fill_value) - self.min(axis, fill_value)
+
+    # Array methods ---------------------------------------
+    conj = conjugate = _arraymethod('conjugate')
+    copy = _arraymethod('copy')
+    diagonal = _arraymethod('diagonal')
+    take = _arraymethod('take')
+    ravel = _arraymethod('ravel')
+    transpose = _arraymethod('transpose')
+    T = property(fget=lambda self:self.transpose())
+    swapaxes = _arraymethod('swapaxes')
+    clip = _arraymethod('clip', onmask=False)
+    compress = _arraymethod('compress')
+    copy = _arraymethod('copy')
+    squeeze = _arraymethod('squeeze')
+    #--------------------------------------------
+    def tolist(self, fill_value=None):
+        """Copies the data portion of the array to a hierarchical python list and
+    returns that list. Data items are converted to the nearest compatible Python
+    type.
+    Masked values are converted to `fill_value`. If `fill_value` is None, the
+    corresponding entries in the output list will be None.
+    """
+        if fill_value is not None:
+            return self.filled(fill_value).tolist()
+        result = self.filled().tolist()
+        if self._mask is nomask:
+            return result
+        if self.ndim == 0:
+            return [None]
+        elif self.ndim == 1:
+            maskedidx = self._mask.nonzero()[0].tolist()
+            [operator.setitem(result,i,None) for i in maskedidx]
+        else:
+            for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
+                tmp = result
+                for i in idx[:-1]:
+                    tmp = tmp[i]
+                tmp[idx[-1]] = None
+        return result
+
+
+    #........................
+    def tostring(self, fill_value=None):
+        """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string.
+
+    Keyword arguments:
+        order      : order of the data item in the copy {"C","F","A"} (default "C")
+        fill_value : value used in lieu of missing data
+
+    Construct a Python string containing the raw bytes in the array. The order
+    of the data in arrays with ndim > 1 is specified by the 'order' keyword and
+    this keyword overrides the order of the array. The
+    choices are:
+
+        "C"       -- C order (row major)
+        "Fortran" -- Fortran order (column major)
+        "Any"     -- Current order of array.
+        None      -- Same as "Any"
+
+    Masked data are filled with fill_value. If fill_value is None, the data-type-
+    dependent default is used."""
+        return self.filled(fill_value).tostring()
+    #--------------------------------------------
+    # Backwards Compatibility. Heck...
+    @property
+    def data(self):
+        """Returns the `_data` part of the MaskedArray."""
+        return self._data
+    def raw_data(self):
+        """Returns the `_data` part of the MaskedArray.
+You should really use `data` instead..."""
+        return self._data
+    #--------------------------------------------
+    # Pickling
+    def __getstate__(self):
+        "Returns the internal state of the masked array, for pickling purposes."
+        state = (1,
+                 self.shape,
+                 self.dtype,
+                 self.flags.fnc,
+                 self._data.tostring(),
+                 getmaskarray(self).tostring(),
+                 self._fill_value,
+                 )
+        return state
+    #
+    def __setstate__(self, state):
+        """Restores the internal state of the masked array, for pickling purposes.
+    `state` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
+
+        - class name
+        - a tuple giving the shape of the data
+        - a typecode for the data
+        - a binary string for the data
+        - a binary string for the mask.
+            """
+        (ver, shp, typ, isf, raw, msk, flv) = state
+        ndarray.__setstate__(self, (shp, typ, isf, raw))
+        self._mask.__setstate__((shp, dtype(bool), isf, msk))
+        self.fill_value = flv
+    #
+    def __reduce__(self):
+        """Returns a 3-tuple for pickling a MaskedArray."""
+        return (_mareconstruct,
+                (self.__class__, self._baseclass, (0,), 'b', ),
+                self.__getstate__())
+
+
+def _mareconstruct(subtype, baseclass, baseshape, basetype,):
+    """Internal function that builds a new MaskedArray from the information stored
+in a pickle."""
+    _data = ndarray.__new__(baseclass, baseshape, basetype)
+    _mask = ndarray.__new__(ndarray, baseshape, 'b1')
+    return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False)
+#MaskedArray.__dump__ = dump
+#MaskedArray.__dumps__ = dumps
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Shortcuts ---
+#####---------------------------------------------------------------------------
+def isMaskedArray(x):
+    "Is x a masked array, that is, an instance of MaskedArray?"
+    return isinstance(x, MaskedArray)
+isarray = isMaskedArray
+isMA = isMaskedArray  #backward compatibility
+#masked = MaskedArray(0, int, mask=1)
+masked_singleton = MaskedArray(0, dtype=int_, mask=True)
+masked = masked_singleton
+
+masked_array = MaskedArray
+def array(data, dtype=None, copy=False, order=False, mask=nomask, subok=True,
+          keep_mask=True, small_mask=True, hard_mask=None, fill_value=None):
+    """array(data, dtype=None, copy=True, order=False, mask=nomask,
+             keep_mask=True, small_mask=True, fill_value=None)
+Acts as shortcut to MaskedArray, with options in a different order for convenience.
+And backwards compatibility...
+    """
+    #TODO: we should try to put 'order' somwehere
+    return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, subok=subok,
+                       keep_mask=keep_mask, small_mask=small_mask,
+                       hard_mask=hard_mask, fill_value=fill_value)
+
+def is_masked(x):
+    """Returns whether x has some masked values."""
+    m = getmask(x)
+    if m is nomask:
+        return False
+    elif m.any():
+        return True
+    return False
+
+
+#####---------------------------------------------------------------------------
+#---- --- Extrema functions ---
+#####---------------------------------------------------------------------------
+class _extrema_operation(object):
+    "Generic class for maximum/minimum functions."
+    def __call__(self, a, b=None):
+        "Executes the call behavior."
+        if b is None:
+            return self.reduce(a)
+        return where(self.compare(a, b), a, b)
+    #.........
+    def reduce(self, target, axis=None):
+        """Reduces target along the given axis."""
+        m = getmask(target)
+        if axis is not None:
+            kargs = { 'axis' : axis }
+        else:
+            kargs = {}
+            target = target.ravel()
+            if not (m is nomask):
+                m = m.ravel()
+        if m is nomask:
+            t = self.ufunc.reduce(target, **kargs)
+        else:
+            target = target.filled(self.fill_value_func(target)).view(type(target))
+            t = self.ufunc.reduce(target, **kargs)
+            m = umath.logical_and.reduce(m, **kargs)
+            if hasattr(t, '_mask'):
+                t._mask = m
+            elif m:
+                t = masked
+        return t
+    #.........
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = logical_or.outer(ma, mb)
+        result = self.ufunc.outer(filled(a), filled(b))
+        result._mask = m
+        return result
+#............................
+class _minimum_operation(_extrema_operation):
+    "Object to calculate minima"
+    def __init__ (self):
+        """minimum(a, b) or minimum(a)
+In one argument case, returns the scalar minimum.
+        """
+        self.ufunc = umath.minimum
+        self.afunc = amin
+        self.compare = less
+        self.fill_value_func = minimum_fill_value
+#............................
+class _maximum_operation(_extrema_operation):
+    "Object to calculate maxima"
+    def __init__ (self):
+        """maximum(a, b) or maximum(a)
+           In one argument case returns the scalar maximum.
+        """
+        self.ufunc = umath.maximum
+        self.afunc = amax
+        self.compare = greater
+        self.fill_value_func = maximum_fill_value
+#..........................................................
+def min(array, axis=None, out=None):
+    """Returns the minima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return minimum(array)
+    else:
+        return minimum.reduce(array, axis)
+#............................
+def max(obj, axis=None, out=None):
+    """Returns the maxima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return maximum(obj)
+    else:
+        return maximum.reduce(obj, axis)
+#.............................
+def ptp(obj, axis=None):
+    """a.ptp(axis=None) =  a.max(axis)-a.min(axis)"""
+    try:
+        return obj.max(axis)-obj.min(axis)
+    except AttributeError:
+        return max(obj, axis=axis) - min(obj, axis=axis)
+
+
+#####---------------------------------------------------------------------------
+#---- --- Definition of functions from the corresponding methods ---
+#####---------------------------------------------------------------------------
+class _frommethod:
+    """Defines functions from existing MaskedArray methods.
+:ivar _methodname (String): Name of the method to transform.
+    """
+    def __init__(self, methodname):
+        self._methodname = methodname
+        self.__doc__ = self.getdoc()
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self._methodname).__doc__
+        except:
+            return getattr(numpy, self._methodname).__doc__
+    def __call__(self, a, *args, **params):
+        if isinstance(a, MaskedArray):
+            return getattr(a, self._methodname).__call__(*args, **params)
+        #FIXME ----
+        #As x is not a MaskedArray, we transform it to a ndarray with asarray
+        #... and call the corresponding method.
+        #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
+        #we end up with a "SystemError: NULL result without error in PyObject_Call"
+        #A dirty trick is then to call the initial numpy function...
+        method = getattr(fromnumeric.asarray(a), self._methodname)
+        try:
+            return method(*args, **params)
+        except SystemError:
+            return getattr(numpy,self._methodname).__call__(a, *args, **params)
+
+all = _frommethod('all')
+anomalies = anom = _frommethod('anom')
+any = _frommethod('any')
+conjugate = _frommethod('conjugate')
+ids = _frommethod('ids')
+nonzero = _frommethod('nonzero')
+diagonal = _frommethod('diagonal')
+maximum = _maximum_operation()
+mean = _frommethod('mean')
+minimum = _minimum_operation ()
+product = _frommethod('prod')
+ptp = _frommethod('ptp')
+ravel = _frommethod('ravel')
+repeat = _frommethod('repeat')
+std = _frommethod('std')
+sum = _frommethod('sum')
+swapaxes = _frommethod('swapaxes')
+take = _frommethod('take')
+var = _frommethod('var')
+
+#..............................................................................
+def power(a, b, third=None):
+    """Computes a**b elementwise.
+    Masked values are set to 1."""
+    if third is not None:
+        raise MAError, "3-argument power not supported."
+    ma = getmask(a)
+    mb = getmask(b)
+    m = mask_or(ma, mb)
+    fa = filled(a, 1)
+    fb = filled(b, 1)
+    if fb.dtype.char in typecodes["Integer"]:
+        return masked_array(umath.power(fa, fb), m)
+    md = make_mask((fa < 0), small_mask=1)
+    m = mask_or(m, md)
+    if m is nomask:
+        return masked_array(umath.power(fa, fb))
+    else:
+        fa[m] = 1
+        return masked_array(umath.power(fa, fb), m)
+
+#..............................................................................
+def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None):
+    """Returns an array of indices that sort 'a' along the specified axis.
+    Masked values are filled beforehand to `fill_value`.
+    If `fill_value` is None, uses the default for the data type.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `kind` : String *['quicksort']*
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+    Returns: array of indices that sort 'a' along the specified axis.
+
+    This method executes an indirect sort along the given axis using the
+    algorithm specified by the kind keyword. It returns an array of indices of
+    the same shape as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case
+    performance, need for work space, and whether they are stable. A stable
+    sort keeps items with the same key in the same relative order. The three
+    available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argsort(kind=kind, order=order)
+    return d.argsort(axis, kind=kind, order=order)
+
+def argmin(a, axis=None, fill_value=None):
+    """Returns the array of indices for the minimum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    return d.argmin(axis=axis)
+
+def argmax(a, axis=None, fill_value=None):
+    """Returns the array of indices for the maximum values of `a` along the
+    specified axis.
+    Masked values are treated as if they had the value `fill_value`.
+    If `fill_value` is None, the default for the data type is used.
+    Returns a numpy array.
+
+:Keywords:
+    `axis` : Integer *[None]*
+        Axis to be indirectly sorted (default -1)
+    `fill_value` : var *[None]*
+        Default filling value. If None, uses the data type default.
+    """
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+        try:
+            fill_value = - fill_value
+        except:
+            pass
+    d = filled(a, fill_value)
+    return d.argmax(axis=axis)
+
+def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+    """
+    Sort a along the given axis.
+
+Keyword arguments:
+
+axis  -- axis to be sorted (default -1)
+kind  -- sorting algorithm (default 'quicksort')
+         Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+order -- If a has fields defined, then the order keyword can be the
+         field name to sort on or a list (or tuple) of field names
+         to indicate the order that fields should be used to define
+         the sort.
+endwith--Boolean flag indicating whether missing values (if any) should
+         be forced in the upper indices (at the end of the array) or
+         lower indices (at the beginning).
+
+Returns: None.
+
+This method sorts 'a' in place along the given axis using the algorithm
+specified by the kind keyword.
+
+The various sorts may characterized by average speed, worst case
+performance, need for work space, and whether they are stable. A stable
+sort keeps items with the same key in the same relative order and is most
+useful when used with argsort where the key might differ from the items
+being sorted. The three available algorithms have the following properties:
+
+|------------------------------------------------------|
+|    kind   | speed |  worst case | work space | stable|
+|------------------------------------------------------|
+|'quicksort'|   1   | O(n^2)      |     0      |   no  |
+|'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+|'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+|------------------------------------------------------|
+
+All the sort algorithms make temporary copies of the data when the sort is
+not along the last axis. Consequently, sorts along the last axis are faster
+and use less space than sorts along other axis.
+
+"""
+    a = numeric.asanyarray(a)
+    if fill_value is None:
+        if endwith:
+            filler = minimum_fill_value(a)
+        else:
+            filler = maximum_fill_value(a)
+    else:
+        filler = fill_value
+#    return
+    indx = numpy.indices(a.shape).tolist()
+    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+    return a[indx]
+
+def compressed(x):
+    """Returns a compressed version of a masked array (or just the array if it
+    wasn't masked first)."""
+    if getmask(x) is None:
+        return x
+    else:
+        return x.compressed()
+
+def count(a, axis = None):
+    "Count of the non-masked elements in a, or along a certain axis."
+    a = masked_array(a)
+    return a.count(axis)
+
+def concatenate(arrays, axis=0):
+    "Concatenates the arrays along the given axis"
+    d = numeric.concatenate([filled(a) for a in arrays], axis)
+    rcls = get_masked_subclass(*arrays)
+    data = d.view(rcls)
+    for x in arrays:
+        if getmask(x) is not nomask:
+            break
+    else:
+        return data
+    dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis)
+    dm = make_mask(dm, copy=False, small_mask=True)
+    data._mask = dm
+    return data
+
+def expand_dims(x,axis):
+    """Expand the shape of a by including newaxis before given axis."""
+    result = n_expand_dims(x,axis)
+    if isinstance(x, MaskedArray):
+        new_shape = result.shape
+        result = x.view()
+        result.shape = new_shape
+        if result._mask is not nomask:
+            result._mask.shape = new_shape
+    return result
+
+#......................................
+def left_shift (a, n):
+    "Left shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.left_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.left_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+
+def right_shift (a, n):
+    "Right shift n bits"
+    m = getmask(a)
+    if m is nomask:
+        d = umath.right_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.right_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+#......................................
+def put(a, indices, values, mode='raise'):
+    """Sets storage-indexed locations to corresponding values.
+    Values and indices are filled if necessary."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.put(indices, values, mode=mode)
+    except AttributeError:
+        return fromnumeric.asarray(a).put(indices, values, mode=mode)
+
+def putmask(a, mask, values): #, mode='raise'):
+    """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true.
+If `v` is shorter than `mask`, it will be repeated as necessary.
+In particular `v` can be a scalar or length 1 array."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.putmask(values, mask)
+    except AttributeError:
+        return fromnumeric.asarray(a).putmask(values, mask)
+
+def transpose(a,axes=None):
+    """Returns a view of the array with dimensions permuted according to axes.
+If `axes` is None (default), returns array with dimensions reversed.
+    """
+    #We can't use 'frommethod', as 'transpose' doesn't take keywords
+    try:
+        return a.transpose(axes)
+    except AttributeError:
+        return fromnumeric.asarray(a).transpose(axes)
+
+def reshape(a, new_shape):
+    """Changes the shape of the array `a` to `new_shape`."""
+    #We can't use 'frommethod', it whine about some parameters. Dmmit.
+    try:
+        return a.reshape(new_shape)
+    except AttributeError:
+        return fromnumeric.asarray(a).reshape(new_shape)
+
+def resize(x, new_shape):
+    """resize(a,new_shape) returns a new array with the specified shape.
+    The total size of the original array can be any size.
+    The new array is filled with repeated copies of a. If a was masked, the new
+    array will be masked, and the new mask will be a repetition of the old one.
+    """
+    # We can't use _frommethods here, as N.resize is notoriously whiny.
+    m = getmask(x)
+    if m is not nomask:
+        m = fromnumeric.resize(m, new_shape)
+    result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x))
+    if result.ndim:
+        result._mask = m
+    return result
+
+
+#................................................
+def rank(obj):
+    """Gets the rank of sequence a (the number of dimensions, not a matrix rank)
+The rank of a scalar is zero."""
+    return fromnumeric.rank(filled(obj))
+#
+def shape(obj):
+    """Returns the shape of `a` (as a function call which also works on nested sequences).
+    """
+    return fromnumeric.shape(filled(obj))
+#
+def size(obj, axis=None):
+    """Returns the number of elements in the array along the given axis,
+or in the sequence if `axis` is None.
+    """
+    return fromnumeric.size(filled(obj), axis)
+#................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Extra functions ---
+#####--------------------------------------------------------------------------
+def where (condition, x, y):
+    """where(condition, x, y) is x where condition is nonzero, y otherwise.
+       condition must be convertible to an integer array.
+       Answer is always the shape of condition.
+       The type depends on x and y. It is integer if both x and y are
+       the value masked.
+    """
+    fc = filled(not_equal(condition, 0), 0)
+    xv = filled(x)
+    xm = getmask(x)
+    yv = filled(y)
+    ym = getmask(y)
+    d = numeric.choose(fc, (yv, xv))
+    md = numeric.choose(fc, (ym, xm))
+    m = getmask(condition)
+    m = make_mask(mask_or(m, md), copy=False, small_mask=True)
+    return masked_array(d, mask=m)
+
+def choose (indices, t, out=None, mode='raise'):
+    "Returns array shaped like indices with elements chosen from t"
+    #TODO: implement options `out` and `mode`, if possible.
+    def fmask (x):
+        "Returns the filled array, or True if ``masked``."
+        if x is masked:
+            return 1
+        return filled(x)
+    def nmask (x):
+        "Returns the mask, True if ``masked``, False if ``nomask``."
+        if x is masked:
+            return 1
+        m = getmask(x)
+        if m is nomask:
+            return 0
+        return m
+    c = filled(indices, 0)
+    masks = [nmask(x) for x in t]
+    a = [fmask(x) for x in t]
+    d = numeric.choose(c, a)
+    m = numeric.choose(c, masks)
+    m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1)
+    return masked_array(d, mask=m)
+
+def round_(a, decimals=0, out=None):
+    """Returns reference to result. Copies a and rounds to 'decimals' places.
+
+    Keyword arguments:
+        decimals -- number of decimals to round to (default 0). May be negative.
+        out -- existing array to use for output (default copy of a).
+
+    Return:
+        Reference to out, where None specifies a copy of the original array a.
+
+    Round to the specified number of decimals. When 'decimals' is negative it
+    specifies the number of positions to the left of the decimal point. The
+    real and imaginary parts of complex numbers are rounded separately.
+    Nothing is done if the array is not of float type and 'decimals' is greater
+    than or equal to 0."""
+    result = fromnumeric.round_(filled(a), decimals, out)
+    if isinstance(a,MaskedArray):
+        result = result.view(type(a))
+        result._mask = a._mask
+    else:
+        result = result.view(MaskedArray)
+    return result
+
+def arange(start, stop=None, step=1, dtype=None):
+    """Just like range() except it returns a array whose type can be specified
+    by the keyword argument dtype.
+    """
+    return array(numeric.arange(start, stop, step, dtype),mask=nomask)
+
+def inner(a, b):
+    """inner(a,b) returns the dot product of two arrays, which has
+    shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the
+    product of the elements from the last dimensions of a and b.
+    Masked elements are replace by zeros.
+    """
+    fa = filled(a, 0)
+    fb = filled(b, 0)
+    if len(fa.shape) == 0:
+        fa.shape = (1,)
+    if len(fb.shape) == 0:
+        fb.shape = (1,)
+    return masked_array(numeric.inner(fa, fb))
+innerproduct = inner
+
+def outer(a, b):
+    """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))"""
+    fa = filled(a, 0).ravel()
+    fb = filled(b, 0).ravel()
+    d = numeric.outer(fa, fb)
+    ma = getmask(a)
+    mb = getmask(b)
+    if ma is nomask and mb is nomask:
+        return masked_array(d)
+    ma = getmaskarray(a)
+    mb = getmaskarray(b)
+    m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
+    return masked_array(d, mask=m)
+outerproduct = outer
+
+def allequal (a, b, fill_value=True):
+    """
+Returns `True` if all entries of  a and b are equal, using
+fill_value as a truth value where either or both are masked.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    if m is nomask:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        return d.all()
+    elif fill_value:
+        x = filled(a)
+        y = filled(b)
+        d = umath.equal(x, y)
+        dm = array(d, mask=m, copy=False)
+        return dm.filled(True).all(None)
+    else:
+        return False
+
+def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+    """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances.
+If `fill_value` is True, masked values are considered equal.
+If `fill_value` is False, masked values considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error `atol` comes into play for those elements of `b`
+ that are very small or zero; it says how small `a` must be also.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    d1 = filled(a)
+    d2 = filled(b)
+    x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
+    y = filled(array(d2, copy=0, mask=m), 1).astype(float)
+    d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+    return fromnumeric.alltrue(fromnumeric.ravel(d))
+
+#..............................................................................
+def asarray(a, dtype=None):
+    """asarray(data, dtype) = array(data, dtype, copy=0)
+Returns `a` as an masked array.
+No copy is performed if `a` is already an array.
+Subclasses are converted to base class MaskedArray.
+    """
+    return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
+
+def empty(new_shape, dtype=float):
+    """empty((d1,...,dn),dtype=float,order='C')
+Returns a new array of shape (d1,...,dn) and given type with all its
+entries uninitialized. This can be faster than zeros."""
+    return numeric.empty(new_shape, dtype).view(MaskedArray)
+
+def empty_like(a):
+    """empty_like(a)
+Returns an empty (uninitialized) array of the shape and typecode of a.
+Note that this does NOT initialize the returned array.
+If you require your array to be initialized, you should use zeros_like()."""
+    return numeric.empty_like(a).view(MaskedArray)
+
+def ones(new_shape, dtype=float):
+    """ones(shape, dtype=None)
+Returns an array of the given dimensions, initialized to all ones."""
+    return numeric.ones(new_shape, dtype).view(MaskedArray)
+
+def zeros(new_shape, dtype=float):
+    """zeros(new_shape, dtype=None)
+Returns an array of the given dimensions, initialized to all zeros."""
+    return numeric.zeros(new_shape, dtype).view(MaskedArray)
+
+#####--------------------------------------------------------------------------
+#---- --- Pickling ---
+#####--------------------------------------------------------------------------
+def dump(a,F):
+    """Pickles the MaskedArray `a` to the file `F`.
+`F` can either be the handle of an exiting file, or a string representing a file name.
+    """
+    if not hasattr(F,'readline'):
+        F = open(F,'w')
+    return cPickle.dump(a,F)
+
+def dumps(a):
+    """Returns a string corresponding to the pickling of the MaskedArray."""
+    return cPickle.dumps(a)
+
+def load(F):
+    """Wrapper around ``cPickle.load`` which accepts either a file-like object or
+ a filename."""
+    if not hasattr(F, 'readline'):
+        F = open(F,'r')
+    return cPickle.load(F)
+
+def loads(strg):
+    "Loads a pickle from the current string."""
+    return cPickle.loads(strg)
+
+
+################################################################################
+
+if __name__ == '__main__':
+    from testutils import assert_equal, assert_almost_equal
+    if 1:
+        narray = numpy.array
+        pi = numpy.pi
+        x = narray([1.,1.,1.,-2., pi/2., 4., 5., -10., 10., 1., 2., 3.])
+        y = narray([5.,0.,3., 2., -1.,  -4., 0., -10., 10., 1., 0., 3.])
+        a10 = 10.
+        m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
+        m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1]
+        xm = masked_array(x, mask=m1)
+        ym = masked_array(y, mask=m2)
+
+    #
+    if 1:
+        n = [0,0,1,0,1]
+        m = make_mask(n)
+        m2 = make_mask(m)
+        assert(m is m2)
+        m3 = make_mask(m, copy=1)
+        assert(m is not m3)
+
+        x1 = numpy.arange(5)
+        y1 = array(x1, mask=m)
+        #assert( y1._data is x1)
+        assert_equal(y1._data.__array_interface__, x1.__array_interface__)
+        assert( allequal(x1,y1.raw_data()))
+        #assert( y1.mask is m)
+        assert_equal(y1._mask.__array_interface__, m.__array_interface__)
+
+        y1a = array(y1)
+        #assert( y1a.raw_data() is y1.raw_data())
+        assert( y1a._data.__array_interface__ == y1._data.__array_interface__)
+        assert( y1a.mask is y1.mask)
+
+        y2 = array(x1, mask=m)
+        #assert( y2.raw_data() is x1)
+        assert (y2._data.__array_interface__ == x1.__array_interface__)
+        #assert( y2.mask is m)
+        assert (y2._mask.__array_interface__ == m.__array_interface__)
+        assert( y2[2] is masked)
+        y2[2] = 9
+        assert( y2[2] is not masked)
+        #assert( y2.mask is not m)
+        assert (y2._mask.__array_interface__ != m.__array_interface__)
+        assert( allequal(y2.mask, 0))

Added: trunk/scipy/sandbox/maskedarray/alternative_versions/test_mrecarray.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/alternative_versions/test_mrecarray.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/alternative_versions/test_mrecarray.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,128 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for mrecarray.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_mrecarray.py 68 2007-01-10 17:46:04Z backtopop $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: backtopop $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 68 $"
+__date__     = '$Date: 2007-01-10 12:46:04 -0500 (Wed, 10 Jan 2007) $'
+
+import types
+
+import numpy as N
+import numpy.core.fromnumeric  as fromnumeric
+from numpy.testing import NumpyTest, NumpyTestCase
+from numpy.testing.utils import build_err_msg
+
+import maskedarray.testutils
+from maskedarray.testutils import *
+
+import maskedarray.core as MA
+import maskedarray.mrecords
+from maskedarray.mrecords import mrecarray, fromarrays, fromtextfile, fromrecords
+
+
+#..............................................................................
+class TestMrecarray(NumpyTestCase):
+    "Base test class for MaskedArrays."
+    def __init__(self, *args, **kwds):
+        NumpyTestCase.__init__(self, *args, **kwds)
+        self.setup()
+
+    def setup(self):
+        "Generic setup"
+        d = N.arange(5)
+        m = MA.make_mask([1,0,0,1,1])
+        base_d = N.r_[d,d[::-1]].reshape(2,-1).T
+        base_m = N.r_[[m, m[::-1]]].T
+        base = MA.array(base_d, mask=base_m)
+        mrec = fromarrays(base.T,)
+        self.data = [d, m, mrec]
+
+    def test_get(self):
+        "Tests fields retrieval"
+        [d, m, mrec] = self.data
+        assert_equal(mrec.f0, MA.array(d,mask=m))
+        assert_equal(mrec.f1, MA.array(d[::-1],mask=m[::-1]))
+        assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]])).all())
+        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+        assert_equal(mrec.f0, mrec['f0'])
+
+    def test_set(self):
+        "Tests setting fields/attributes."
+        [d, m, mrec] = self.data
+        mrec.f0_data = 5
+        assert_equal(mrec['f0_data'], [5,5,5,5,5])
+        mrec.f0 = 1
+        assert_equal(mrec['f0_data'], [1]*5)
+        assert_equal(mrec['f0_mask'], [0]*5)
+        mrec.f1 = MA.masked
+        assert_equal(mrec.f1.mask, [1]*5)
+        assert_equal(mrec['f1_mask'], [1]*5)
+        mrec._mask = MA.masked
+        assert_equal(mrec['f1_mask'], [1]*5)
+        assert_equal(mrec['f0_mask'],mrec['f1_mask'])
+        mrec._mask = MA.nomask
+        assert_equal(mrec['f1_mask'], [0]*5)
+        assert_equal(mrec['f0_mask'],mrec['f1_mask'])
+
+    def test_hardmask(self):
+        "Test hardmask"
+        [d, m, mrec] = self.data
+        print mrec._mask
+        mrec.harden_mask()
+        assert(mrec._hardmask)
+        mrec._mask = nomask
+        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+        mrec.soften_mask()
+        assert(not mrec._hardmask)
+        mrec._mask = nomask
+        assert_equal(mrec['f1_mask'], [0]*5)
+        assert_equal(mrec['f0_mask'],mrec['f1_mask'])
+
+    def test_fromtextfile(self):
+        "Tests reading from a text file."
+        fcontent = """#
+'One (S)','Two (I)','Three (F)','Four (M)','Five (-)','Six (C)'
+'strings',1,1.0,'mixed column',,1
+'with embedded "double quotes"',2,2.0,1.0,,1
+'strings',3,3.0E5,3,,1
+'strings',4,-1e-10,,,1
+"""
+        import os
+        from datetime import datetime
+        fname = 'tmp%s' % datetime.now().strftime("%y%m%d%H%M%S%s")
+        f = open(fname, 'w')
+        f.write(fcontent)
+        f.close()
+        mrectxt = fromtextfile(fname,delimitor=',',varnames='ABCDEFG')
+        os.unlink(fname)
+        #
+        assert(isinstance(mrectxt, mrecarray))
+        assert_equal(mrectxt.F, [1,1,1,1])
+        assert_equal(mrectxt.E._mask, [1,1,1,1])
+        assert_equal(mrectxt.C, [1,2,3.e+5,-1e-10])
+
+    def test_fromrecords(self):
+        "Test from recarray."
+        [d, m, mrec] = self.data
+        nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]])
+        mrecfr = fromrecords(nrec.tolist())
+        assert_equal(mrecfr.f0, mrec.f0)
+        assert_equal(mrecfr.dtype, mrec.dtype)
+        #....................
+        mrecfr = fromrecords(nrec)
+        assert_equal(mrecfr.f0, mrec.f0)
+        assert_equal(mrecfr.dtype, mrec.dtype)
+        #....................
+        tmp = mrec[::-1] #.tolist()
+        mrecfr = fromrecords(tmp)
+        assert_equal(mrecfr.f0, mrec.f0[::-1])
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    NumpyTest().run()

Added: trunk/scipy/sandbox/maskedarray/bench.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/bench.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/bench.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,198 @@
+#! python
+
+import timeit
+#import IPython.ipapi
+#ip = IPython.ipapi.get()
+#from IPython import ipmagic
+import numpy
+import maskedarray
+from maskedarray import filled
+from maskedarray.testutils import assert_equal
+
+
+#####---------------------------------------------------------------------------
+#---- --- Global variables ---
+#####---------------------------------------------------------------------------
+
+# Small arrays ..................................
+xs = numpy.random.uniform(-1,1,6).reshape(2,3)
+ys = numpy.random.uniform(-1,1,6).reshape(2,3)
+zs = xs + 1j * ys
+m1 = [[True, False, False], [False, False, True]]
+m2 = [[True, False, True], [False, False, True]]
+nmxs = numpy.ma.array(xs, mask=m1)
+nmys = numpy.ma.array(ys, mask=m2)
+nmzs = numpy.ma.array(zs, mask=m1)
+mmxs = maskedarray.array(xs, mask=m1)
+mmys = maskedarray.array(ys, mask=m2)
+mmzs = maskedarray.array(zs, mask=m1)
+# Big arrays ....................................
+xl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+yl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+zl = xl + 1j * yl
+maskx = xl > 0.8
+masky = yl < -0.8
+nmxl = numpy.ma.array(xl, mask=maskx)
+nmyl = numpy.ma.array(yl, mask=masky)
+nmzl = numpy.ma.array(zl, mask=maskx)
+mmxl = maskedarray.array(xl, mask=maskx, shrink=True)
+mmyl = maskedarray.array(yl, mask=masky, shrink=True)
+mmzl = maskedarray.array(zl, mask=maskx, shrink=True)
+
+#####---------------------------------------------------------------------------
+#---- --- Functions ---
+#####---------------------------------------------------------------------------
+
+def timer(s, v='', nloop=500, nrep=3):
+    units = ["s", "ms", "\xb5s", "ns"]
+    scaling = [1, 1e3, 1e6, 1e9]
+    print "%s : %-50s : " % (v,s),
+    varnames = ["%ss,nm%ss,mm%ss,%sl,nm%sl,mm%sl" % tuple(x*6) for x in 'xyz']
+    setup = 'from __main__ import numpy, maskedarray, %s' % ','.join(varnames)
+    Timer = timeit.Timer(stmt=s, setup=setup)
+    best = min(Timer.repeat(nrep, nloop)) / nloop
+    if best > 0.0:
+        order = min(-int(numpy.floor(numpy.log10(best)) // 3), 3)
+    else:
+        order = 3
+    print "%d loops, best of %d: %.*g %s per loop" % (nloop, nrep,
+                                                      3,
+                                                      best * scaling[order],
+                                                      units[order])
+#    ip.magic('timeit -n%i %s' % (nloop,s))
+
+
+
+def compare_functions_1v(func, nloop=500, test=True,
+                       xs=xs, nmxs=nmxs, mmxs=mmxs,
+                       xl=xl, nmxl=nmxl, mmxl=mmxl):
+    funcname = func.__name__
+    print "-"*50
+    print "%s on small arrays" % funcname
+    if test:
+        assert_equal(filled(eval("numpy.ma.%s(nmxs)" % funcname),0),
+                     filled(eval("maskedarray.%s(mmxs)" % funcname),0))
+    for (module, data) in zip(("numpy", "numpy.ma","maskedarray"),
+                              ("xs","nmxs","mmxs")):
+        timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop)
+    #
+    print "%s on large arrays" % funcname
+    if test:
+        assert_equal(filled(eval("numpy.ma.%s(nmxl)" % funcname),0),
+                     filled(eval("maskedarray.%s(mmxl)" % funcname),0))
+    for (module, data) in zip(("numpy", "numpy.ma","maskedarray"),
+                              ("xl","nmxl","mmxl")):
+        timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop)
+    return
+
+def compare_methods(methodname, args, vars='x', nloop=500, test=True,
+                    xs=xs, nmxs=nmxs, mmxs=mmxs,
+                    xl=xl, nmxl=nmxl, mmxl=mmxl):
+    print "-"*50
+    print "%s on small arrays" % methodname
+    if test:
+        assert_equal(filled(eval("nm%ss.%s(%s)" % (vars,methodname,args)),0),
+                     filled(eval("mm%ss.%s(%s)" % (vars,methodname,args)),0))
+    for (data, ver) in zip(["nm%ss" % vars, "mm%ss" % vars], ('numpy.ma   ','maskedarray')):
+        timer("%(data)s.%(methodname)s(%(args)s)" % locals(), v=ver, nloop=nloop)
+    #
+    print "%s on large arrays" % methodname
+    if test:
+        assert_equal(filled(eval("nm%sl.%s(%s)" % (vars,methodname,args)),0),
+                     filled(eval("mm%sl.%s(%s)" % (vars,methodname,args)),0))
+    for (data, ver) in zip(["nm%sl" % vars, "mm%sl" % vars], ('numpy.ma   ','maskedarray')):
+        timer("%(data)s.%(methodname)s(%(args)s)" % locals(), v=ver, nloop=nloop)
+    return
+
+def compare_functions_2v(func, nloop=500, test=True,
+                       xs=xs, nmxs=nmxs, mmxs=mmxs,
+                       ys=ys, nmys=nmys, mmys=mmys,
+                       xl=xl, nmxl=nmxl, mmxl=mmxl,
+                       yl=yl, nmyl=nmyl, mmyl=mmyl):
+    funcname = func.__name__
+    print "-"*50
+    print "%s on small arrays" % funcname
+    if test:
+        assert_equal(filled(eval("numpy.ma.%s(nmxs,nmys)" % funcname),0),
+                     filled(eval("maskedarray.%s(mmxs,mmys)" % funcname),0))
+    for (module, data) in zip(("numpy", "numpy.ma","maskedarray"),
+                              ("xs,ys","nmxs,nmys","mmxs,mmys")):
+        timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop)
+    #
+    print "%s on large arrays" % funcname
+    if test:
+        assert_equal(filled(eval("numpy.ma.%s(nmxl, nmyl)" % funcname),0),
+                     filled(eval("maskedarray.%s(mmxl, mmyl)" % funcname),0))
+    for (module, data) in zip(("numpy", "numpy.ma","maskedarray"),
+                              ("xl,yl","nmxl,nmyl","mmxl,mmyl")):
+        timer("%(module)s.%(funcname)s(%(data)s)" % locals(), v="%11s" % module, nloop=nloop)
+    return
+
+
+###############################################################################
+
+
+################################################################################
+if __name__ == '__main__':
+#    # Small arrays ..................................
+#    xs = numpy.random.uniform(-1,1,6).reshape(2,3)
+#    ys = numpy.random.uniform(-1,1,6).reshape(2,3)
+#    zs = xs + 1j * ys
+#    m1 = [[True, False, False], [False, False, True]]
+#    m2 = [[True, False, True], [False, False, True]]
+#    nmxs = numpy.ma.array(xs, mask=m1)
+#    nmys = numpy.ma.array(ys, mask=m2)
+#    nmzs = numpy.ma.array(zs, mask=m1)
+#    mmxs = maskedarray.array(xs, mask=m1)
+#    mmys = maskedarray.array(ys, mask=m2)
+#    mmzs = maskedarray.array(zs, mask=m1)
+#    # Big arrays ....................................
+#    xl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+#    yl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+#    zl = xl + 1j * yl
+#    maskx = xl > 0.8
+#    masky = yl < -0.8
+#    nmxl = numpy.ma.array(xl, mask=maskx)
+#    nmyl = numpy.ma.array(yl, mask=masky)
+#    nmzl = numpy.ma.array(zl, mask=maskx)
+#    mmxl = maskedarray.array(xl, mask=maskx, shrink=True)
+#    mmyl = maskedarray.array(yl, mask=masky, shrink=True)
+#    mmzl = maskedarray.array(zl, mask=maskx, shrink=True)
+#
+    compare_functions_1v(numpy.sin)
+    compare_functions_1v(numpy.log)
+    compare_functions_1v(numpy.sqrt)
+    #....................................................................
+    compare_functions_2v(numpy.multiply)
+    compare_functions_2v(numpy.divide)
+    compare_functions_2v(numpy.power)
+    #....................................................................
+    compare_methods('ravel','', nloop=1000)
+    compare_methods('conjugate','','z', nloop=1000)
+    compare_methods('transpose','', nloop=1000)
+    compare_methods('compressed','', nloop=1000)
+    compare_methods('__getitem__','0', nloop=1000)
+    compare_methods('__getitem__','(0,0)', nloop=1000)
+    compare_methods('__getitem__','[0,-1]', nloop=1000)
+    compare_methods('__setitem__','0, 17', nloop=1000, test=False)
+    compare_methods('__setitem__','(0,0), 17', nloop=1000, test=False)
+    #....................................................................
+    print "-"*50
+    print "__setitem__ on small arrays"
+    timer('nmxs.__setitem__((-1,0),numpy.ma.masked)', 'numpy.ma   ',nloop=10000)
+    timer('mmxs.__setitem__((-1,0),maskedarray.masked)', 'maskedarray',nloop=10000)
+    print "-"*50
+    print "__setitem__ on large arrays"
+    timer('nmxl.__setitem__((-1,0),numpy.ma.masked)', 'numpy.ma   ',nloop=10000)
+    timer('mmxl.__setitem__((-1,0),maskedarray.masked)', 'maskedarray',nloop=10000)
+    #....................................................................
+    print "-"*50
+    print "where on small arrays"
+    assert_equal(eval("numpy.ma.where(nmxs>2,nmxs,nmys)"),
+                 eval("maskedarray.where(mmxs>2, mmxs,mmys)"))
+    timer('numpy.ma.where(nmxs>2,nmxs,nmys)', 'numpy.ma   ',nloop=1000)
+    timer('maskedarray.where(mmxs>2, mmxs,mmys)', 'maskedarray',nloop=1000)
+    print "-"*50
+    print "where on large arrays"
+    timer('numpy.ma.where(nmxl>2,nmxl,nmyl)', 'numpy.ma   ',nloop=100)
+    timer('maskedarray.where(mmxl>2, mmxl,mmyl)', 'maskedarray',nloop=100)

Added: trunk/scipy/sandbox/maskedarray/core.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/core.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/core.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,2979 @@
+# pylint: disable-msg=E1002
+"""MA: a facility for dealing with missing observations
+MA is generally used as a numpy.array look-alike.
+by Paul F. Dubois.
+
+Copyright 1999, 2000, 2001 Regents of the University of California.
+Released for unlimited redistribution.
+Adapted for numpy_core 2005 by Travis Oliphant and
+(mainly) Paul Dubois.
+
+Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
+pgmdevlist_AT_gmail_DOT_com
+Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com)
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: core.py 3639 2007-12-13 03:39:17Z pierregm $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3639 $"
+__date__     = '$Date: 2007-12-12 19:39:17 -0800 (Wed, 12 Dec 2007) $'
+
+__docformat__ = "restructuredtext en"
+
+__all__ = ['MAError', 'MaskType', 'MaskedArray',
+           'bool_', 'complex_', 'float_', 'int_', 'object_',
+           'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
+               'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
+               'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
+               'arctanh', 'argmax', 'argmin', 'argsort', 'around',
+               'array', 'asarray','asanyarray',
+           'bitwise_and', 'bitwise_or', 'bitwise_xor',
+           'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
+               'cos', 'cosh', 'count',
+           'default_fill_value', 'diagonal', 'divide', 'dump', 'dumps',
+           'empty', 'empty_like', 'equal', 'exp',
+           'fabs', 'fmod', 'filled', 'floor', 'floor_divide','fix_invalid',
+           'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
+           'ids', 'inner', 'innerproduct',
+               'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
+           'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
+               'logical_and', 'logical_not', 'logical_or', 'logical_xor',
+           'make_mask', 'make_mask_none', 'mask_or', 'masked',
+               'masked_array', 'masked_equal', 'masked_greater',
+               'masked_greater_equal', 'masked_inside', 'masked_less',
+               'masked_less_equal', 'masked_not_equal', 'masked_object',
+               'masked_outside', 'masked_print_option', 'masked_singleton',
+               'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
+               'minimum', 'multiply',
+           'negative', 'nomask', 'nonzero', 'not_equal',
+           'ones', 'outer', 'outerproduct',
+           'power', 'product', 'ptp', 'put', 'putmask',
+           'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
+               'right_shift', 'round_',
+           'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
+               'subtract', 'sum', 'swapaxes',
+           'take', 'tan', 'tanh', 'transpose', 'true_divide',
+           'var', 'where',
+           'zeros']
+
+import sys
+import types
+import cPickle
+import operator
+#
+import numpy
+from numpy import bool_, complex_, float_, int_, object_, str_
+
+import numpy.core.umath as umath
+import numpy.core.fromnumeric  as fromnumeric
+import numpy.core.numeric as numeric
+import numpy.core.numerictypes as ntypes
+from numpy import bool_, dtype, typecodes, amax, amin, ndarray
+from numpy import expand_dims as n_expand_dims
+from numpy import array as narray
+import warnings
+
+
+MaskType = bool_
+nomask = MaskType(0)
+
+divide_tolerance = 1.e-35
+numpy.seterr(all='ignore')
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Exceptions ---
+#####--------------------------------------------------------------------------
+class MAError(Exception):
+    "Class for MA related errors."
+    def __init__ (self, args=None):
+        "Creates an exception."
+        Exception.__init__(self,args)
+        self.args = args
+    def __str__(self):
+        "Calculates the string representation."
+        return str(self.args)
+    __repr__ = __str__
+
+#####--------------------------------------------------------------------------
+#---- --- Filling options ---
+#####--------------------------------------------------------------------------
+# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
+default_filler = {'b': True,
+                  'c' : 1.e20 + 0.0j,
+                  'f' : 1.e20,
+                  'i' : 999999,
+                  'O' : '?',
+                  'S' : 'N/A',
+                  'u' : 999999,
+                  'V' : '???',
+                  }
+max_filler = ntypes._minvals
+max_filler.update([(k,-numpy.inf) for k in [numpy.float32, numpy.float64]])
+min_filler = ntypes._maxvals
+min_filler.update([(k,numpy.inf) for k in [numpy.float32, numpy.float64]])
+if 'float128' in ntypes.typeDict:
+    max_filler.update([(numpy.float128,-numpy.inf)])
+    min_filler.update([(numpy.float128, numpy.inf)])
+
+def default_fill_value(obj):
+    """Calculates the default fill value for the argument object.
+    """
+    if hasattr(obj,'dtype'):
+        defval = default_filler[obj.dtype.kind]
+    elif isinstance(obj, numeric.dtype):
+        defval = default_filler[obj.kind]
+    elif isinstance(obj, float):
+        defval = default_filler['f']
+    elif isinstance(obj, int) or isinstance(obj, long):
+        defval = default_filler['i']
+    elif isinstance(obj, str):
+        defval = default_filler['S']
+    elif isinstance(obj, complex):
+        defval = default_filler['c']
+    else:
+        defval = default_filler['O']
+    return defval
+
+def minimum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the minimum of ``obj``."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = min_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return min_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return min_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return min_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return min_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def maximum_fill_value(obj):
+    "Calculates the default fill value suitable for taking the maximum of ``obj``."
+    if hasattr(obj, 'dtype'):
+        objtype = obj.dtype
+        filler = max_filler[objtype]
+        if filler is None:
+            raise TypeError, 'Unsuitable type for calculating minimum.'
+        return filler
+    elif isinstance(obj, float):
+        return max_filler[ntypes.typeDict['float_']]
+    elif isinstance(obj, int):
+        return max_filler[ntypes.typeDict['int_']]
+    elif isinstance(obj, long):
+        return max_filler[ntypes.typeDict['uint']]
+    elif isinstance(obj, numeric.dtype):
+        return max_filler[obj]
+    else:
+        raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def set_fill_value(a, fill_value):
+    """Sets the filling value of a, if a is a masked array.
+Otherwise, does nothing.
+
+*Returns*:
+    None
+    """
+    if isinstance(a, MaskedArray):
+        a.set_fill_value(fill_value)
+    return
+
+def get_fill_value(a):
+    """Returns the filling value of a, if any.
+Otherwise, returns the default filling value for that type.
+    """
+    if isinstance(a, MaskedArray):
+        result = a.fill_value
+    else:
+        result = default_fill_value(a)
+    return result
+
+def common_fill_value(a, b):
+    """Returns the common filling value of a and b, if any.
+    If a and b have different filling values, returns None."""
+    t1 = get_fill_value(a)
+    t2 = get_fill_value(b)
+    if t1 == t2:
+        return t1
+    return None
+
+#####--------------------------------------------------------------------------
+def filled(a, value = None):
+    """Returns a as an array with masked data replaced by value.
+If value is None, get_fill_value(a) is used instead.
+If a is already a ndarray, a itself is returned.
+
+*Parameters*:
+    a : {var}
+        An input object.
+    value : {var}, optional
+        Filling value. If not given, the output of get_fill_value(a) is used instead.
+
+*Returns*:
+    A ndarray.
+
+    """
+    if hasattr(a, 'filled'):
+        return a.filled(value)
+    elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
+        return a
+    elif isinstance(a, dict):
+        return narray(a, 'O')
+    else:
+        return narray(a)
+
+#####--------------------------------------------------------------------------
+def get_masked_subclass(*arrays):
+    """Returns the youngest subclass of MaskedArray from a list of (masked) arrays.
+In case of siblings, the first takes over."""
+    if len(arrays) == 1:
+        arr = arrays[0]
+        if isinstance(arr, MaskedArray):
+            rcls = type(arr)
+        else:
+            rcls = MaskedArray
+    else:
+        arrcls = [type(a) for a in arrays]
+        rcls = arrcls[0]
+        if not issubclass(rcls, MaskedArray):
+            rcls = MaskedArray
+        for cls in arrcls[1:]:
+            if issubclass(cls, rcls):
+                rcls = cls
+    return rcls
+
+#####--------------------------------------------------------------------------
+def get_data(a, subok=True):
+    """Returns the _data part of a (if any), or a as a ndarray.
+
+*Parameters* :
+    a : {ndarray}
+        A ndarray or a subclass of.
+    subok : {boolean}
+        Whether to force the output to a 'pure' ndarray (False) or to return a subclass
+        of ndarray if approriate (True).
+    """
+    data = getattr(a, '_data', numpy.array(a, subok=subok))
+    if not subok:
+        return data.view(ndarray)
+    return data
+getdata = get_data
+
+def fix_invalid(a, copy=True, fill_value=None):
+    """Returns (a copy of) a where invalid data (nan/inf) are masked and replaced
+by fill_value.
+Note that a copy is performed by default (just in case...).
+    
+*Parameters*:
+    a : {ndarray}
+        A (subclass of) ndarray.
+    copy : {boolean}
+        Whether to use a copy of a (True) or to fix a in place (False).
+    fill_value : {var}, optional
+        Value used for fixing invalid data.
+        If not given, the output of get_fill_value(a) is used instead.
+
+*Returns* :
+    MaskedArray object
+    """
+    a = masked_array(a, copy=copy, subok=True)
+    invalid = (numpy.isnan(a._data) | numpy.isinf(a._data))
+    a._mask |= invalid
+    if fill_value is None:
+        fill_value = a.fill_value
+    a._data[invalid] = fill_value
+    return a
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Ufuncs ---
+#####--------------------------------------------------------------------------
+ufunc_domain = {}
+ufunc_fills = {}
+
+class _DomainCheckInterval:
+    """Defines a valid interval, so that :
+``domain_check_interval(a,b)(x) = true`` where ``x < a`` or ``x > b``."""
+    def __init__(self, a, b):
+        "domain_check_interval(a,b)(x) = true where x < a or y > b"
+        if (a > b):
+            (a, b) = (b, a)
+        self.a = a
+        self.b = b
+
+    def __call__ (self, x):
+        "Executes the call behavior."
+        return umath.logical_or(umath.greater (x, self.b),
+                                umath.less(x, self.a))
+#............................
+class _DomainTan:
+    """Defines a valid interval for the `tan` function, so that:
+``domain_tan(eps) = True`` where ``abs(cos(x)) < eps``"""
+    def __init__(self, eps):
+        "domain_tan(eps) = true where abs(cos(x)) < eps)"
+        self.eps = eps
+    def __call__ (self, x):
+        "Executes the call behavior."
+        return umath.less(umath.absolute(umath.cos(x)), self.eps)
+#............................
+class _DomainSafeDivide:
+    """Defines a domain for safe division."""
+    def __init__ (self, tolerance=divide_tolerance):
+        self.tolerance = tolerance
+    def __call__ (self, a, b):
+        return umath.absolute(a) * self.tolerance >= umath.absolute(b)
+#............................
+class _DomainGreater:
+    "DomainGreater(v)(x) = true where x <= v"
+    def __init__(self, critical_value):
+        "DomainGreater(v)(x) = true where x <= v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Executes the call behavior."
+        return umath.less_equal(x, self.critical_value)
+#............................
+class _DomainGreaterEqual:
+    "DomainGreaterEqual(v)(x) = true where x < v"
+    def __init__(self, critical_value):
+        "DomainGreaterEqual(v)(x) = true where x < v"
+        self.critical_value = critical_value
+
+    def __call__ (self, x):
+        "Executes the call behavior."
+        return umath.less(x, self.critical_value)
+
+#..............................................................................
+class _MaskedUnaryOperation:
+    """Defines masked version of unary operations, where invalid values are pre-masked.
+
+:IVariables:
+    f : function.
+    fill : Default filling value *[0]*.
+    domain : Default domain *[None]*.
+    """
+    def __init__ (self, mufunc, fill=0, domain=None):
+        """ _MaskedUnaryOperation(aufunc, fill=0, domain=None)
+            aufunc(fill) must be defined
+            self(x) returns aufunc(x)
+            with masked values where domain(x) is true or getmask(x) is true.
+        """
+        self.f = mufunc
+        self.fill = fill
+        self.domain = domain
+        self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
+        self.__name__ = getattr(mufunc, "__name__", str(mufunc))
+        ufunc_domain[mufunc] = domain
+        ufunc_fills[mufunc] = fill
+    #
+    def __call__ (self, a, *args, **kwargs):
+        "Executes the call behavior."
+        m = getmask(a)
+        d1 = get_data(a)
+        if self.domain is not None:
+            dm = narray(self.domain(d1), copy=False)
+            m = numpy.logical_or(m, dm)
+            # The following two lines control the domain filling methods.
+            d1 = d1.copy()
+#            d1[dm] = self.fill
+            numpy.putmask(d1, dm, self.fill)
+        # Take care of the masked singletong first ...
+        if not m.ndim and m:
+            return masked
+        # Get the result..............................
+        if isinstance(a, MaskedArray):
+            result = self.f(d1, *args, **kwargs).view(type(a))
+        else:
+            result = self.f(d1, *args, **kwargs).view(MaskedArray)
+        # Fix the mask if we don't have a scalar
+        if result.ndim > 0:
+            result._mask = m
+        return result
+    #
+    def __str__ (self):
+        return "Masked version of %s. [Invalid values are masked]" % str(self.f)
+
+#..............................................................................
+class _MaskedBinaryOperation:
+    """Defines masked version of binary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+    f : function.
+    fillx : Default filling value for the first argument *[0]*.
+    filly : Default filling value for the second argument *[0]*.
+    domain : Default domain *[None]*.
+    """
+    def __init__ (self, mbfunc, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = mbfunc
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
+        self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
+        ufunc_domain[mbfunc] = None
+        ufunc_fills[mbfunc] = (fillx, filly)
+    #
+    def __call__ (self, a, b, *args, **kwargs):
+        "Execute the call behavior."
+        m = mask_or(getmask(a), getmask(b))
+        (d1, d2) = (get_data(a), get_data(b))
+        result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b))
+        if result.size > 1:
+            if m is not nomask:
+                result._mask = make_mask_none(result.shape)
+                result._mask.flat = m
+        elif m:
+            return masked
+        return result
+    #
+    def reduce (self, target, axis=0, dtype=None):
+        """Reduces `target` along the given `axis`."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = MaskedArray
+        m = getmask(target)
+        t = filled(target, self.filly)
+        if t.shape == ():
+            t = t.reshape(1)
+            if m is not nomask:
+                m = make_mask(m, copy=1)
+                m.shape = (1,)
+        if m is nomask:
+            return self.f.reduce(t, axis).view(tclass)
+        t = t.view(tclass)
+        t._mask = m
+        tr = self.f.reduce(getdata(t), axis, dtype=dtype or t.dtype)
+        mr = umath.logical_and.reduce(m, axis)
+        tr = tr.view(tclass)
+        if mr.ndim > 0:
+            tr._mask = mr
+            return tr
+        elif mr:
+            return masked
+        return tr
+
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = umath.logical_or.outer(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        rcls = get_masked_subclass(a,b)
+#        d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
+        d = self.f.outer(getdata(a), getdata(b)).view(rcls)
+        if d.ndim > 0:
+            d._mask = m
+        return d
+
+    def accumulate (self, target, axis=0):
+        """Accumulates `target` along `axis` after filling with y fill value."""
+        if isinstance(target, MaskedArray):
+            tclass = type(target)
+        else:
+            tclass = masked_array
+        t = filled(target, self.filly)
+        return self.f.accumulate(t, axis).view(tclass)
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+
+#..............................................................................
+class _DomainedBinaryOperation:
+    """Defines binary operations that have a domain, like divide.
+
+They have no reduce, outer or accumulate.
+
+:IVariables:
+    f : function.
+    domain : Default domain.
+    fillx : Default filling value for the first argument *[0]*.
+    filly : Default filling value for the second argument *[0]*.
+    """
+    def __init__ (self, dbfunc, domain, fillx=0, filly=0):
+        """abfunc(fillx, filly) must be defined.
+           abfunc(x, filly) = x for all x to enable reduce.
+        """
+        self.f = dbfunc
+        self.domain = domain
+        self.fillx = fillx
+        self.filly = filly
+        self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
+        self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
+        ufunc_domain[dbfunc] = domain
+        ufunc_fills[dbfunc] = (fillx, filly)
+
+    def __call__(self, a, b):
+        "Execute the call behavior."
+        ma = getmask(a)
+        mb = getmask(b)
+        d1 = getdata(a)
+        d2 = get_data(b)
+        t = narray(self.domain(d1, d2), copy=False)
+        if t.any(None):
+            mb = mask_or(mb, t)
+            # The following two lines control the domain filling
+            d2 = d2.copy()
+            numpy.putmask(d2, t, self.filly)
+        m = mask_or(ma, mb)
+        if (not m.ndim) and m:
+            return masked
+        result =  self.f(d1, d2).view(get_masked_subclass(a,b))
+        if result.ndim > 0:
+            result._mask = m
+        return result
+
+    def __str__ (self):
+        return "Masked version of " + str(self.f)
+
+#..............................................................................
+# Unary ufuncs
+exp = _MaskedUnaryOperation(umath.exp)
+conjugate = _MaskedUnaryOperation(umath.conjugate)
+sin = _MaskedUnaryOperation(umath.sin)
+cos = _MaskedUnaryOperation(umath.cos)
+tan = _MaskedUnaryOperation(umath.tan)
+arctan = _MaskedUnaryOperation(umath.arctan)
+arcsinh = _MaskedUnaryOperation(umath.arcsinh)
+sinh = _MaskedUnaryOperation(umath.sinh)
+cosh = _MaskedUnaryOperation(umath.cosh)
+tanh = _MaskedUnaryOperation(umath.tanh)
+abs = absolute = _MaskedUnaryOperation(umath.absolute)
+fabs = _MaskedUnaryOperation(umath.fabs)
+negative = _MaskedUnaryOperation(umath.negative)
+floor = _MaskedUnaryOperation(umath.floor)
+ceil = _MaskedUnaryOperation(umath.ceil)
+around = _MaskedUnaryOperation(fromnumeric.round_)
+logical_not = _MaskedUnaryOperation(umath.logical_not)
+# Domained unary ufuncs .......................................................
+sqrt = _MaskedUnaryOperation(umath.sqrt, 0.0,
+                             _DomainGreaterEqual(0.0))
+log = _MaskedUnaryOperation(umath.log, 1.0,
+                            _DomainGreater(0.0))
+log10 = _MaskedUnaryOperation(umath.log10, 1.0,
+                              _DomainGreater(0.0))
+tan = _MaskedUnaryOperation(umath.tan, 0.0,
+                            _DomainTan(1.e-35))
+arcsin = _MaskedUnaryOperation(umath.arcsin, 0.0,
+                               _DomainCheckInterval(-1.0, 1.0))
+arccos = _MaskedUnaryOperation(umath.arccos, 0.0,
+                               _DomainCheckInterval(-1.0, 1.0))
+arccosh = _MaskedUnaryOperation(umath.arccosh, 1.0,
+                                _DomainGreaterEqual(1.0))
+arctanh = _MaskedUnaryOperation(umath.arctanh, 0.0,
+                                _DomainCheckInterval(-1.0+1e-15, 1.0-1e-15))
+# Binary ufuncs ...............................................................
+add = _MaskedBinaryOperation(umath.add)
+subtract = _MaskedBinaryOperation(umath.subtract)
+multiply = _MaskedBinaryOperation(umath.multiply, 1, 1)
+arctan2 = _MaskedBinaryOperation(umath.arctan2, 0.0, 1.0)
+equal = _MaskedBinaryOperation(umath.equal)
+equal.reduce = None
+not_equal = _MaskedBinaryOperation(umath.not_equal)
+not_equal.reduce = None
+less_equal = _MaskedBinaryOperation(umath.less_equal)
+less_equal.reduce = None
+greater_equal = _MaskedBinaryOperation(umath.greater_equal)
+greater_equal.reduce = None
+less = _MaskedBinaryOperation(umath.less)
+less.reduce = None
+greater = _MaskedBinaryOperation(umath.greater)
+greater.reduce = None
+logical_and = _MaskedBinaryOperation(umath.logical_and)
+alltrue = _MaskedBinaryOperation(umath.logical_and, 1, 1).reduce
+logical_or = _MaskedBinaryOperation(umath.logical_or)
+sometrue = logical_or.reduce
+logical_xor = _MaskedBinaryOperation(umath.logical_xor)
+bitwise_and = _MaskedBinaryOperation(umath.bitwise_and)
+bitwise_or = _MaskedBinaryOperation(umath.bitwise_or)
+bitwise_xor = _MaskedBinaryOperation(umath.bitwise_xor)
+hypot = _MaskedBinaryOperation(umath.hypot)
+# Domained binary ufuncs ......................................................
+divide = _DomainedBinaryOperation(umath.divide, _DomainSafeDivide(), 0, 1)
+true_divide = _DomainedBinaryOperation(umath.true_divide,
+                                        _DomainSafeDivide(), 0, 1)
+floor_divide = _DomainedBinaryOperation(umath.floor_divide,
+                                         _DomainSafeDivide(), 0, 1)
+remainder = _DomainedBinaryOperation(umath.remainder,
+                                      _DomainSafeDivide(), 0, 1)
+fmod = _DomainedBinaryOperation(umath.fmod, _DomainSafeDivide(), 0, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Mask creation functions ---
+#####--------------------------------------------------------------------------
+def get_mask(a):
+    """Returns the mask of a, if any, or nomask.
+To get a full array of booleans of the same shape as a, use getmaskarray.
+    """
+    return getattr(a, '_mask', nomask)
+getmask = get_mask
+
+def getmaskarray(a):
+    """Returns the mask of a, if any, or a boolean array of the shape of a, full of False.
+    """
+    m = getmask(a)
+    if m is nomask:
+        m = make_mask_none(fromnumeric.shape(a))
+    return m
+
+def is_mask(m):
+    """Returns True if m is a legal mask.
+Does not check contents, only type.
+    """
+    try:
+        return m.dtype.type is MaskType
+    except AttributeError:
+        return False
+#
+def make_mask(m, copy=False, shrink=True, flag=None):
+    """Returns m as a mask, creating a copy if necessary or requested.
+The function can accept any sequence of integers or nomask.
+Does not check that contents must be 0s and 1s.
+
+*Parameters*:
+    m : {ndarray}
+        Potential mask.
+    copy : {boolean}
+        Whether to return a copy of m (True) or m itself (False).
+    shrink : {boolean}
+        Whether to shrink m to nomask if all its values are False.
+    """
+    if flag is not None:
+        warnings.warn("The flag 'flag' is now called 'shrink'!",
+                      DeprecationWarning)
+        shrink = flag
+    if m is nomask:
+        return nomask
+    elif isinstance(m, ndarray):
+        m = filled(m, True)
+        if m.dtype.type is MaskType:
+            if copy:
+                result = narray(m, dtype=MaskType, copy=copy)
+            else:
+                result = m
+        else:
+            result = narray(m, dtype=MaskType)
+    else:
+        result = narray(filled(m, True), dtype=MaskType)
+    # Bas les masques !
+    if shrink and not result.any():
+        return nomask
+    else:
+        return result
+
+def make_mask_none(s):
+    """Returns a mask of shape s, filled with False.
+
+*Parameters*:
+    s : {tuple}
+        A tuple indicating the shape of the final mask.
+    """
+    result = numeric.zeros(s, dtype=MaskType)
+    return result
+
+def mask_or (m1, m2, copy=False, shrink=True):
+    """Returns the combination of two masks m1 and m2.
+The masks are combined with the *logical_or* operator, treating nomask as False.
+The result may equal m1 or m2 if the other is nomask.
+
+*Parameters*:
+    m1 : {ndarray}
+        First mask.
+    m2 : {ndarray}
+        Second mask
+    copy : {boolean}
+        Whether to return a copy.
+    shrink : {boolean}
+        Whether to shrink m to nomask if all its values are False.
+     """
+    if m1 is nomask:
+        return make_mask(m2, copy=copy, shrink=shrink)
+    if m2 is nomask:
+        return make_mask(m1, copy=copy, shrink=shrink)
+    if m1 is m2 and is_mask(m1):
+        return m1
+    return make_mask(umath.logical_or(m1, m2), copy=copy, shrink=shrink)
+
+#####--------------------------------------------------------------------------
+#--- --- Masking functions ---
+#####--------------------------------------------------------------------------
+def masked_where(condition, a, copy=True):
+    """Returns a as an array masked where condition is true.
+Masked values of a or condition are kept.
+
+*Parameters*:
+    condition : {ndarray}
+        Masking condition.
+    a : {ndarray}
+        Array to mask.
+    copy : {boolean}
+        Whether to return a copy of a (True) or modify a in place.
+    """
+    cond = filled(condition,1)
+    a = narray(a, copy=copy, subok=True)
+    if hasattr(a, '_mask'):
+        cond = mask_or(cond, a._mask)
+        cls = type(a)
+    else:
+        cls = MaskedArray
+    result = a.view(cls)
+    result._mask = cond
+    return result
+
+def masked_greater(x, value, copy=True):
+    "Shortcut to masked_where, with condition = (x > value)."
+    return masked_where(greater(x, value), x, copy=copy)
+
+def masked_greater_equal(x, value, copy=True):
+    "Shortcut to masked_where, with condition = (x >= value)."
+    return masked_where(greater_equal(x, value), x, copy=copy)
+
+def masked_less(x, value, copy=True):
+    "Shortcut to masked_where, with condition = (x < value)."
+    return masked_where(less(x, value), x, copy=copy)
+
+def masked_less_equal(x, value, copy=True):
+    "Shortcut to masked_where, with condition = (x <= value)."
+    return masked_where(less_equal(x, value), x, copy=copy)
+
+def masked_not_equal(x, value, copy=True):
+    "Shortcut to masked_where, with condition = (x != value)."
+    return masked_where((x != value), x, copy=copy)
+
+#
+def masked_equal(x, value, copy=True):
+    """Shortcut to masked_where, with condition = (x == value).
+For floating point, consider `masked_values(x, value)` instead.
+    """
+    return masked_where((x == value), x, copy=copy)
+#    d = filled(x, 0)
+#    c = umath.equal(d, value)
+#    m = mask_or(c, getmask(x))
+#    return array(d, mask=m, copy=copy)
+
+def masked_inside(x, v1, v2, copy=True):
+    """Shortcut to masked_where, where condition is True for x inside the interval
+[v1,v2] (v1 <= x <= v2).
+The boundaries v1 and v2 can be given in either order.
+
+*Note*:
+    The array x is prefilled with its filling value.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf >= v1) & (xf <= v2)
+    return masked_where(condition, x, copy=copy)
+
+def masked_outside(x, v1, v2, copy=True):
+    """Shortcut to masked_where, where condition is True for x outside the interval
+[v1,v2] (x < v1)|(x > v2).
+The boundaries v1 and v2 can be given in either order.
+
+*Note*:
+    The array x is prefilled with its filling value.
+    """
+    if v2 < v1:
+        (v1, v2) = (v2, v1)
+    xf = filled(x)
+    condition = (xf < v1) | (xf > v2)
+    return masked_where(condition, x, copy=copy)
+
+#
+def masked_object(x, value, copy=True):
+    """Masks the array x where the data are exactly equal to value.
+
+This function is suitable only for object arrays: for floating point, please use
+``masked_values`` instead.
+
+*Notes*:
+    The mask is set to `nomask` if posible.
+    """
+    if isMaskedArray(x):
+        condition = umath.equal(x._data, value)
+        mask = x._mask
+    else:
+        condition = umath.equal(fromnumeric.asarray(x), value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, shrink=True))
+    return masked_array(x, mask=mask, copy=copy, fill_value=value)
+
+def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
+    """Masks the array x where the data are approximately equal to value
+(abs(x - value) <= atol+rtol*abs(value)).
+Suitable only for floating points. For integers, please use ``masked_equal``.
+The mask is set to nomask if posible.
+
+*Parameters*:
+    x : {ndarray}
+        Array to fill.
+    value : {float}
+        Masking value.
+    rtol : {float}
+        Tolerance parameter.
+    atol : {float}, *[1e-8]*
+        Tolerance parameter.
+    copy : {boolean}
+        Whether to return a copy of x.
+    """
+    abs = umath.absolute
+    xnew = filled(x, value)
+    if issubclass(xnew.dtype.type, numeric.floating):
+        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+        mask = getattr(x, '_mask', nomask)
+    else:
+        condition = umath.equal(xnew, value)
+        mask = nomask
+    mask = mask_or(mask, make_mask(condition, shrink=True))
+    return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
+
+def masked_invalid(a, copy=True):
+    """Masks the array for invalid values (nans or infs).
+    Any preexisting mask is conserved."""
+    a = narray(a, copy=copy, subok=True)
+    condition = (numpy.isnan(a) | numpy.isinf(a))
+    if hasattr(a, '_mask'):
+        condition = mask_or(condition, a._mask)
+        cls = type(a)
+    else:
+        cls = MaskedArray
+    result = a.view(cls)
+    result._mask = cond
+    return result
+
+
+#####--------------------------------------------------------------------------
+#---- --- Printing options ---
+#####--------------------------------------------------------------------------
+class _MaskedPrintOption:
+    """Handles the string used to represent missing data in a masked array."""
+    def __init__ (self, display):
+        "Creates the masked_print_option object."
+        self._display = display
+        self._enabled = True
+
+    def display(self):
+        "Displays the string to print for masked values."
+        return self._display
+
+    def set_display (self, s):
+        "Sets the string to print for masked values."
+        self._display = s
+
+    def enabled(self):
+        "Is the use of the display value enabled?"
+        return self._enabled
+
+    def enable(self, shrink=1):
+        "Set the enabling shrink to `shrink`."
+        self._enabled = shrink
+
+    def __str__ (self):
+        return str(self._display)
+
+    __repr__ = __str__
+
+#if you single index into a masked location you get this object.
+masked_print_option = _MaskedPrintOption('--')
+
+#####--------------------------------------------------------------------------
+#---- --- MaskedArray class ---
+#####--------------------------------------------------------------------------
+
+#...............................................................................
+class _arraymethod(object):
+    """Defines a wrapper for basic array methods.
+Upon call, returns a masked array, where the new _data array is the output of
+the corresponding method called on the original _data.
+
+If onmask is True, the new mask is the output of the method called on the initial
+mask. Otherwise, the new mask is just a reference to the initial mask.
+
+:IVariables:
+    _name : String
+        Name of the function to apply on data.
+    _onmask : {boolean} *[True]*
+        Whether the mask must be processed also (True) or left alone (False).
+    obj : Object
+        The object calling the arraymethod
+    """
+    def __init__(self, funcname, onmask=True):
+        self._name = funcname
+        self._onmask = onmask
+        self.obj = None
+        self.__doc__ = self.getdoc()
+    #
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        methdoc = getattr(ndarray, self._name, None)
+        methdoc = getattr(numpy, self._name, methdoc)
+        if methdoc is not None:
+            return methdoc.__doc__
+    #
+    def __get__(self, obj, objtype=None):
+        self.obj = obj
+        return self
+    #
+    def __call__(self, *args, **params):
+        methodname = self._name
+        data = self.obj._data
+        mask = self.obj._mask
+        cls = type(self.obj)
+        result = getattr(data, methodname)(*args, **params).view(cls)
+        result._update_from(self.obj)
+        #result._shrinkmask = self.obj._shrinkmask
+        if result.ndim:
+            if not self._onmask:
+                result.__setmask__(mask)
+            elif mask is not nomask:
+                result.__setmask__(getattr(mask, methodname)(*args, **params))
+        else:
+            if mask.ndim and mask.all():
+                return masked
+        return result
+#..........................................................
+
+class FlatIter(object):
+    "Defines an interator."
+    def __init__(self, ma):
+        self.ma = ma
+        self.ma_iter = numpy.asarray(ma).flat
+
+        if ma._mask is nomask:
+            self.maskiter = None
+        else:
+            self.maskiter = ma._mask.flat
+
+    def __iter__(self):
+        return self
+
+    ### This won't work is ravel makes a copy
+    def __setitem__(self, index, value):
+        a = self.ma.ravel()
+        a[index] = value
+
+    def next(self):
+        d = self.ma_iter.next()
+        if self.maskiter is not None and self.maskiter.next():
+            d = masked
+        return d
+
+
+class MaskedArray(numeric.ndarray):
+    """Arrays with possibly masked values.
+Masked values of True exclude the corresponding element from any computation.
+
+Construction:
+    x = MaskedArray(data, mask=nomask, dtype=None, copy=True, fill_value=None,
+              mask = nomask, fill_value=None, shrink=True)
+
+*Parameters*:
+    data : {var}
+        Input data.
+    mask : {nomask, sequence}
+        Mask.
+        Must be convertible to an array of booleans with the same shape as data:
+        True indicates a masked (eg., invalid) data.
+    dtype : {dtype}
+        Data type of the output. If None, the type of the data argument is used.
+        If dtype is not None and different from data.dtype, a copy is performed.
+    copy : {boolean}
+        Whether to copy the input data (True), or to use a reference instead.
+        Note: data are NOT copied by default.
+    fill_value : {var}
+        Value used to fill in the masked values when necessary. If None, a default
+        based on the datatype is used.
+    keep_mask : {True, boolean}
+        Whether to combine mask with the mask of the input data, if any (True),
+        or to use only mask for the output (False).
+    hard_mask : {False, boolean}
+        Whether to use a hard mask or not. With a hard mask, masked values cannot
+        be unmasked.
+    subok : {True, boolean}
+        Whether to return a subclass of MaskedArray (if possible) or a plain
+        MaskedArray.
+    """
+
+    __array_priority__ = 15
+    _defaultmask = nomask
+    _defaulthardmask = False
+    _baseclass =  numeric.ndarray
+
+    def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
+                keep_mask=True, hard_mask=False, flag=None,
+                subok=True, **options):
+        """Creates a new masked array from scratch.
+    Note: you can also create an array with the .view(MaskedArray) method...
+        """
+        if flag is not None:
+            warnings.warn("The flag 'flag' is now called 'shrink'!",
+                          DeprecationWarning)
+            shrink = flag
+        # Process data............
+        _data = narray(data, dtype=dtype, copy=copy, subok=True)
+        _baseclass = getattr(data, '_baseclass', type(_data))
+        _basedict = getattr(data, '_basedict', getattr(data, '__dict__', None))
+        if not isinstance(data, MaskedArray) or not subok:
+            _data = _data.view(cls)
+        else:
+            _data = _data.view(type(data))
+        # Backwards compatibility w/ numpy.core.ma .......
+        if hasattr(data,'_mask') and not isinstance(data, ndarray):
+            _data._mask = data._mask
+            _sharedmask = True
+        # Process mask ...........
+        if mask is nomask:
+            if not keep_mask:
+                _data._mask = nomask
+            if copy:
+                _data._mask = _data._mask.copy()
+                _data._sharedmask = False
+            else:
+                _data._sharedmask = True
+        else:
+            mask = narray(mask, dtype=MaskType, copy=copy)
+            if mask.shape != _data.shape:
+                (nd, nm) = (_data.size, mask.size)
+                if nm == 1:
+                    mask = numeric.resize(mask, _data.shape)
+                elif nm == nd:
+                    mask = fromnumeric.reshape(mask, _data.shape)
+                else:
+                    msg = "Mask and data not compatible: data size is %i, "+\
+                          "mask size is %i."
+                    raise MAError, msg % (nd, nm)
+                copy = True
+            if _data._mask is nomask:
+                _data._mask = mask
+                _data._sharedmask = not copy
+            else:
+                if not keep_mask:
+                    _data._mask = mask
+                    _data._sharedmask = not copy
+                else:
+                    _data._mask = umath.logical_or(mask, _data._mask)
+                    _data._sharedmask = False
+
+        # Update fill_value.......
+        if fill_value is None:
+            _data._fill_value = getattr(data, '_fill_value',
+                                        default_fill_value(_data))
+        else:
+            _data._fill_value = fill_value
+        # Process extra options ..
+        _data._hardmask = hard_mask
+        _data._baseclass = _baseclass
+        _data._basedict = _basedict
+        return _data
+    #
+    def _update_from(self, obj):
+        """Copies some attributes of obj to self.
+        """
+        self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask)
+        self._sharedmask = getattr(obj, '_sharedmask', False)
+        if obj is not None:
+            self._baseclass = getattr(obj, '_baseclass', type(obj))
+        else:
+            self._baseclass = ndarray
+        self._fill_value = getattr(obj, '_fill_value', None)
+        return
+    #........................
+    def __array_finalize__(self,obj):
+        """Finalizes the masked array.
+        """
+        # Get main attributes .........
+        self._mask = getattr(obj, '_mask', nomask)
+        self._update_from(obj)
+        # Update special attributes ...
+        self._basedict = getattr(obj, '_basedict', getattr(obj, '__dict__', None))
+        if self._basedict is not None:
+            self.__dict__.update(self._basedict)
+        # Finalize the mask ...........
+        if self._mask is not nomask:
+            self._mask.shape = self.shape
+        return
+    #..................................
+    def __array_wrap__(self, obj, context=None):
+        """Special hook for ufuncs.
+Wraps the numpy array and sets the mask according to context.
+        """
+        result = obj.view(type(self))
+        result._update_from(self)
+        #..........
+        if context is not None:
+            result._mask = result._mask.copy()
+            (func, args, _) = context
+            m = reduce(mask_or, [getmask(arg) for arg in args])
+            # Get the domain mask................
+            domain = ufunc_domain.get(func, None)
+            if domain is not None:
+                if len(args) > 2:
+                    d = reduce(domain, args)
+                else:
+                    d = domain(*args)
+                # Fill the result where the domain is wrong
+                try:
+                    # Binary domain: take the last value
+                    fill_value = ufunc_fills[func][-1]
+                except TypeError:
+                    # Unary domain: just use this one
+                    fill_value = ufunc_fills[func]
+                except KeyError:
+                    # Domain not recognized, use fill_value instead
+                    fill_value = self.fill_value
+                result = result.copy()
+                numpy.putmask(result, d, fill_value)
+                # Update the mask
+                if m is nomask:
+                    if d is not nomask:
+                        m = d
+                else:
+                    m |= d
+            # Make sure the mask has the proper size
+            if result.shape == () and m:
+                return masked
+            else:
+                result._mask = m
+                result._sharedmask = False
+        #....
+        return result
+    #.............................................
+    def __getitem__(self, indx):
+        """x.__getitem__(y) <==> x[y]
+Returns the item described by i, as a masked array.
+        """
+        # This test is useful, but we should keep things light...
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        dout = ndarray.__getitem__(self.view(ndarray), indx)
+        m = self._mask
+        if not getattr(dout,'ndim', False):
+            # Just a scalar............
+            if m is not nomask and m[indx]:
+                return masked
+        else:
+            # Force dout to MA ........
+            dout = dout.view(type(self))
+            # Inherit attributes from self
+            dout._update_from(self)
+            # Update the mask if needed
+            if m is not nomask:
+                dout._mask = ndarray.__getitem__(m, indx).reshape(dout.shape)
+#               Note: Don't try to check for m.any(), that'll take too long...
+#                mask = ndarray.__getitem__(m, indx).reshape(dout.shape)
+#                if self._shrinkmask and not m.any():
+#                    dout._mask = nomask
+#                else:
+#                    dout._mask = mask
+        return dout
+    #........................
+    def __setitem__(self, indx, value):
+        """x.__setitem__(i, y) <==> x[i]=y
+Sets item described by index. If value is masked, masks those locations.
+        """
+        if self is masked:
+            raise MAError, 'Cannot alter the masked element.'
+#        if getmask(indx) is not nomask:
+#            msg = "Masked arrays must be filled before they can be used as indices!"
+#            raise IndexError, msg
+        #....
+        if value is masked:
+            m = self._mask
+            if m is nomask:
+                m = numpy.zeros(self.shape, dtype=MaskType)
+            m[indx] = True
+            self._mask = m
+            self._sharedmask = False
+            return
+        #....
+        dval = getdata(value).astype(self.dtype)
+        valmask = getmask(value)
+        if self._mask is nomask:
+            if valmask is not nomask:
+                self._mask = numpy.zeros(self.shape, dtype=MaskType)
+                self._mask[indx] = valmask
+        elif not self._hardmask:
+            # Unshare the mask if necessary to avoid propagation
+            self.unshare_mask()
+            self._mask[indx] = valmask
+        elif hasattr(indx, 'dtype') and (indx.dtype==bool_):
+            indx = indx * umath.logical_not(self._mask)
+        else:
+            mindx = mask_or(self._mask[indx], valmask, copy=True)
+            dindx = self._data[indx]
+            if dindx.size > 1:
+                dindx[~mindx] = dval
+            elif mindx is nomask:
+                dindx = dval
+            dval = dindx
+            self._mask[indx] = mindx
+        # Set data ..........
+        ndarray.__setitem__(self._data,indx,dval)
+    #............................................
+    def __getslice__(self, i, j):
+        """x.__getslice__(i, j) <==> x[i:j]
+Returns the slice described by (i, j).
+The use of negative indices is not supported."""
+        return self.__getitem__(slice(i,j))
+    #........................
+    def __setslice__(self, i, j, value):
+        """x.__setslice__(i, j, value) <==> x[i:j]=value
+Sets the slice (i,j) of a to value. If value is masked, masks those locations.
+        """
+        self.__setitem__(slice(i,j), value)
+    #............................................
+    def __setmask__(self, mask, copy=False):
+        """Sets the mask."""
+        if mask is not nomask:
+            mask = narray(mask, copy=copy, dtype=MaskType)
+#            if self._shrinkmask and not mask.any():
+#                mask = nomask
+        if self._mask is nomask:
+            self._mask = mask
+        elif self._hardmask:
+            if mask is not nomask:
+                self._mask.__ior__(mask)
+        else:
+            # This one is tricky: if we set the mask that way, we may break the
+            # propagation. But if we don't, we end up with a mask full of False
+            # and a test on nomask fails...
+            if mask is nomask:
+                self._mask = nomask
+            else:
+                self.unshare_mask()
+                self._mask.flat = mask
+        if self._mask.shape:
+            self._mask = numeric.reshape(self._mask, self.shape)
+    _set_mask = __setmask__
+    #....
+    def _get_mask(self):
+        """Returns the current mask."""
+        return self._mask
+#        return self._mask.reshape(self.shape)
+    mask = property(fget=_get_mask, fset=__setmask__, doc="Mask")
+    #............................................
+    def harden_mask(self):
+        "Forces the mask to hard."
+        self._hardmask = True
+
+    def soften_mask(self):
+        "Forces the mask to soft."
+        self._hardmask = False
+
+    def unshare_mask(self):
+        "Copies the mask and set the sharedmask flag to False."
+        if self._sharedmask:
+            self._mask = self._mask.copy()
+            self._sharedmask = False
+
+    def shrink_mask(self):
+        "Reduces a mask to nomask when possible."
+        m = self._mask
+        if m.ndim and not m.any():
+            self._mask = nomask
+
+    #............................................
+    def _get_data(self):
+        "Returns the current data, as a view of the original underlying data."
+        return self.view(self._baseclass)
+    _data = property(fget=_get_data)
+    data = property(fget=_get_data)
+
+    def raw_data(self):
+        """Returns the _data part of the MaskedArray.
+DEPRECATED: You should really use ``.data`` instead..."""
+        return self._data
+    #............................................
+    def _get_flat(self):
+        "Returns a flat iterator."
+        return FlatIter(self)
+    #
+    def _set_flat (self, value):
+        "Sets a flattened version of self to value."
+        y = self.ravel()
+        y[:] = value
+    #
+    flat = property(fget=_get_flat, fset=_set_flat,
+                    doc="Flat version of the array.")
+    #............................................
+    def get_fill_value(self):
+        "Returns the filling value."
+        if self._fill_value is None:
+            self._fill_value = default_fill_value(self)
+        return self._fill_value
+
+    def set_fill_value(self, value=None):
+        """Sets the filling value to value.
+If value is None, uses a default based on the data type."""
+        if value is None:
+            value = default_fill_value(self)
+        self._fill_value = value
+
+    fill_value = property(fget=get_fill_value, fset=set_fill_value,
+                          doc="Filling value.")
+
+    def filled(self, fill_value=None):
+        """Returns a copy of self._data, where masked values are filled with
+fill_value.
+
+If fill_value is None, self.fill_value is used instead.
+
+*Note*:
+    + Subclassing is preserved
+    + The result is NOT a MaskedArray !
+
+*Examples*:
+    >>> x = array([1,2,3,4,5], mask=[0,0,1,0,1], fill_value=-999)
+    >>> x.filled()
+    array([1,2,-999,4,-999])
+    >>> type(x.filled())
+    <type 'numpy.ndarray'>
+        """
+        m = self._mask
+        if m is nomask or not m.any():
+            return self._data
+        #
+        if fill_value is None:
+            fill_value = self.fill_value
+        #
+        if self is masked_singleton:
+            result = numeric.asanyarray(fill_value)
+        else:
+            result = self._data.copy()
+            try:
+                numpy.putmask(result, m, fill_value)
+            except (TypeError, AttributeError):
+                fill_value = narray(fill_value, dtype=object)
+                d = result.astype(object)
+                result = fromnumeric.choose(m, (d, fill_value))
+            except IndexError:
+                #ok, if scalar
+                if self._data.shape:
+                    raise
+                elif m:
+                    result = narray(fill_value, dtype=self.dtype)
+                else:
+                    result = self._data
+        return result
+
+    def compressed(self):
+        """Returns a 1-D array of all the non-masked data."""
+        data = ndarray.ravel(self._data).view(type(self))
+        data._update_from(self)
+        if self._mask is not nomask:
+            data = data[numpy.logical_not(ndarray.ravel(self._mask))]
+#        if not self._shrinkmask:
+#            data._mask = numpy.zeros(data.shape, dtype=MaskType)
+        return data
+
+    #............................................
+    def __str__(self):
+        """Calculates the string representation.
+        """
+        if masked_print_option.enabled():
+            f = masked_print_option
+            if self is masked:
+                return str(f)
+            m = self._mask
+            if m is nomask:
+                res = self._data
+            else:
+                if m.shape == ():
+                    if m:
+                        return str(f)
+                    else:
+                        return str(self._data)
+                # convert to object array to make filled work
+#CHECK: the two lines below seem more robust than the self._data.astype
+#                res = numeric.empty(self._data.shape, object_)
+#                numeric.putmask(res,~m,self._data)
+                res = self._data.astype("|O8")
+                res[m] = f
+        else:
+            res = self.filled(self.fill_value)
+        return str(res)
+
+    def __repr__(self):
+        """Calculates the repr representation.
+        """
+        with_mask = """\
+masked_%(name)s(data =
+ %(data)s,
+      mask =
+ %(mask)s,
+      fill_value=%(fill)s)
+"""
+        with_mask1 = """\
+masked_%(name)s(data = %(data)s,
+      mask = %(mask)s,
+      fill_value=%(fill)s)
+"""
+        n = len(self.shape)
+        name = repr(self._data).split('(')[0]
+        if n <= 1:
+            return with_mask1 % {
+                'name': name,
+                'data': str(self),
+                'mask': str(self._mask),
+                'fill': str(self.fill_value),
+                }
+        return with_mask % {
+            'name': name,
+            'data': str(self),
+            'mask': str(self._mask),
+            'fill': str(self.fill_value),
+            }
+    #............................................
+    def __add__(self, other):
+        "Adds other to self, and returns a new masked array."
+        return add(self, other)
+    #
+    def __sub__(self, other):
+        "Subtracts other to self, and returns a new masked array."
+        return subtract(self, other)
+    #
+    def __mul__(self, other):
+        "Multiplies other to self, and returns a new masked array."
+        return multiply(self, other)
+    #
+    def __div__(self, other):
+        "Divides other to self, and returns a new masked array."
+        return divide(self, other)
+    #
+    def __truediv__(self, other):
+        "Divides other to self, and returns a new masked array."
+        return true_divide(self, other)
+    #
+    def __floordiv__(self, other):
+        "Divides other to self, and returns a new masked array."
+        return floor_divide(self, other)
+
+    #............................................
+    def __iadd__(self, other):
+        "Adds other to self in place."
+        ndarray.__iadd__(self._data, getdata(other))
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __isub__(self, other):
+        "Subtracts other from self in place."
+        ndarray.__isub__(self._data, getdata(other))
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __imul__(self, other):
+        "Multiplies self by other in place."
+        ndarray.__imul__(self._data, getdata(other))
+        m = getmask(other)
+        if self._mask is nomask:
+            self._mask = m
+        elif m is not nomask:
+            self._mask += m
+        return self
+    #....
+    def __idiv__(self, other):
+        "Divides self by other in place."
+        other_data = getdata(other)
+        dom_mask = _DomainSafeDivide().__call__(self._data, other_data)
+        other_mask = getmask(other)
+        new_mask = mask_or(other_mask, dom_mask)
+        # The following 3 lines control the domain filling
+        if dom_mask.any():
+            other_data = other_data.copy()
+            numpy.putmask(other_data, dom_mask, 1)
+        ndarray.__idiv__(self._data, other_data)
+        self._mask = mask_or(self._mask, new_mask)
+        return self
+    #............................................
+    def __float__(self):
+        "Converts self to float."
+        if self._mask is not nomask:
+            warnings.warn("Warning: converting a masked element to nan.")
+            return numpy.nan
+            #raise MAError, 'Cannot convert masked element to a Python float.'
+        return float(self.item())
+
+    def __int__(self):
+        "Converts self to int."
+        if self._mask is not nomask:
+            raise MAError, 'Cannot convert masked element to a Python int.'
+        return int(self.item())
+    #............................................
+    def get_imag(self):
+        result = self._data.imag.view(type(self))
+        result.__setmask__(self._mask)
+        return result
+    imag = property(fget=get_imag,doc="Imaginary part")
+
+    def get_real(self):
+        result = self._data.real.view(type(self))
+        result.__setmask__(self._mask)
+        return result
+    real = property(fget=get_real,doc="Real part")
+
+
+    #............................................
+    def count(self, axis=None):
+        """Counts the non-masked elements of the array along the given axis.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to count the non-masked elements. If not given, all the
+        non masked elements are counted.
+
+*Returns*:
+     A masked array where the mask is True where all data are masked.
+     If axis is None, returns either a scalar ot the masked singleton if all values
+     are masked.
+        """
+        m = self._mask
+        s = self.shape
+        ls = len(s)
+        if m is nomask:
+            if ls == 0:
+                return 1
+            if ls == 1:
+                return s[0]
+            if axis is None:
+                return self.size
+            else:
+                n = s[axis]
+                t = list(s)
+                del t[axis]
+                return numeric.ones(t) * n
+        n1 = numpy.size(m, axis)
+        n2 = m.astype(int_).sum(axis)
+        if axis is None:
+            return (n1-n2)
+        else:
+            return masked_array(n1 - n2)
+    #............................................
+    flatten = _arraymethod('flatten')
+#    ravel = _arraymethod('ravel')
+    def ravel(self):
+        """Returns a 1D version of self, as a view."""
+        r = ndarray.ravel(self._data).view(type(self))
+        r._update_from(self)
+        if self._mask is not nomask:
+            r._mask = ndarray.ravel(self._mask).reshape(r.shape)
+        else:
+            r._mask = nomask
+        return r
+    repeat = _arraymethod('repeat')
+    #
+    def reshape (self, *s):
+        """Reshapes the array to shape s.
+
+*Returns*:
+    A new masked array.
+
+*Notes:
+    If you want to modify the shape in place, please use ``a.shape = s``
+        """
+        result = self._data.reshape(*s).view(type(self))
+        result.__dict__.update(self.__dict__)
+        if result._mask is not nomask:
+            result._mask = self._mask.copy()
+            result._mask.shape = result.shape
+        return result
+    #
+    def resize(self, newshape, refcheck=True, order=False):
+        """Attempts to modify the size and the shape of the array in place.
+
+The array must own its own memory and not be referenced by other arrays.
+
+*Returns*:
+    None.
+        """
+        try:
+            self._data.resize(newshape, refcheck, order)
+            if self.mask is not nomask:
+                self._mask.resize(newshape, refcheck, order)
+        except ValueError:
+            raise ValueError("Cannot resize an array that has been referenced "
+                             "or is referencing another array in this way.\n"
+                             "Use the resize function.")
+        return None
+    #
+    def put(self, indices, values, mode='raise'):
+        """Sets storage-indexed locations to corresponding values.
+
+a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
+If values is shorter than indices then it will repeat.
+If values has some masked values, the initial mask is updated in consequence,
+else the corresponding values are unmasked.
+        """
+        m = self._mask
+        # Hard mask: Get rid of the values/indices that fall on masked data
+        if self._hardmask and self._mask is not nomask:
+            mask = self._mask[indices]
+            indices = narray(indices, copy=False)
+            values = narray(values, copy=False, subok=True)
+            values.resize(indices.shape)
+            indices = indices[~mask]
+            values = values[~mask]
+        #....
+        self._data.put(indices, values, mode=mode)
+        #....
+        if m is nomask:
+            m = getmask(values)
+        else:
+            m = m.copy()
+            if getmask(values) is nomask:
+                m.put(indices, False, mode=mode)
+            else:
+                m.put(indices, values._mask, mode=mode)
+            m = make_mask(m, copy=False, shrink=True)
+        self._mask = m
+    #............................................
+    def ids (self):
+        """Returns the addresses of the data and mask areas."""
+        return (self.ctypes.data, self._mask.ctypes.data)
+    #............................................
+    def all(self, axis=None, out=None):
+        """Returns True if all entries along the given axis are True, False otherwise.
+Masked values are considered as True during computation.
+
+*Parameters*
+    axis : {integer}, optional
+        Axis along which the operation is performed.
+        If None, the operation is performed on a flatten array
+    out : {MaskedArray}, optional
+        Alternate optional output.
+        If not None, out should be a valid MaskedArray of the same shape as the
+        output of self._data.all(axis).
+
+*Returns*
+    A masked array, where the mask is True if all data along the axis are masked.
+
+*Notes*
+    An exception is raised if ``out`` is not None and not of the same type as self.
+        """
+        if out is None:
+            d = self.filled(True).all(axis=axis).view(type(self))
+            if d.ndim > 0:
+                d.__setmask__(self._mask.all(axis))
+            return d
+        elif type(out) is not type(self):
+            raise TypeError("The external array should have a type %s (got %s instead)" %\
+                            (type(self), type(out)))
+        self.filled(True).all(axis=axis, out=out)
+        if out.ndim:
+            out.__setmask__(self._mask.all(axis))
+        return out
+
+
+    def any(self, axis=None, out=None):
+        """Returns True if at least one entry along the given axis is True.
+
+Returns False if all entries are False.
+Masked values are considered as True during computation.
+
+*Parameters*
+    axis : {integer}, optional
+        Axis along which the operation is performed.
+        If None, the operation is performed on a flatten array
+    out : {MaskedArray}, optional
+        Alternate optional output.
+        If not None, out should be a valid MaskedArray of the same shape as the
+        output of self._data.all(axis).
+
+*Returns*
+    A masked array, where the mask is True if all data along the axis are masked.
+
+*Notes*
+    An exception is raised if ``out`` is not None and not of the same type as self.
+        """
+        if out is None:
+            d = self.filled(False).any(axis=axis).view(type(self))
+            if d.ndim > 0:
+                d.__setmask__(self._mask.all(axis))
+            return d
+        elif type(out) is not type(self):
+            raise TypeError("The external array should have a type %s (got %s instead)" %\
+                            (type(self), type(out)))
+        self.filled(False).any(axis=axis, out=out)
+        if out.ndim:
+            out.__setmask__(self._mask.all(axis))
+        return out
+
+
+    def nonzero(self):
+        """Returns the indices of the elements of a that are not zero nor masked,
+as a tuple of arrays.
+
+There are as many tuples as dimensions of a, each tuple contains the indices of
+the non-zero elements in that dimension.  The corresponding non-zero values can
+be obtained with ``a[a.nonzero()]``.
+
+To group the indices by element, rather than dimension, use instead:
+``transpose(a.nonzero())``.
+
+The result of this is always a 2d array, with a row for each non-zero element.
+        """
+        return narray(self.filled(0), copy=False).nonzero()
+    #............................................
+    def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
+        """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
+Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
+        """
+        # TODO: What are we doing with `out`?
+        m = self._mask
+        if m is nomask:
+            result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
+                                                    axis2=axis2, out=out)
+            return result.astype(dtype)
+        else:
+            D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
+            return D.astype(dtype).filled(0).sum(axis=None)
+    #............................................
+    def sum(self, axis=None, dtype=None):
+        """Sums the array over the given axis.
+
+Masked elements are set to 0 internally.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+    """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
+        if result.ndim > 0:
+            result.__setmask__(mask)
+        return result
+
+    def cumsum(self, axis=None, dtype=None):
+        """Returns the cumulative sum of the elements of the array along the given axis.
+
+Masked values are set to 0 internally.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+        """
+        result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def prod(self, axis=None, dtype=None):
+        """Returns the product of the elements of the array along the given axis.
+
+Masked elements are set to 1 internally.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+        """
+        if self._mask is nomask:
+            mask = nomask
+        else:
+            mask = self._mask.all(axis)
+            if (not mask.ndim) and mask:
+                return masked
+        result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
+        if result.ndim:
+            result.__setmask__(mask)
+        return result
+    product = prod
+
+    def cumprod(self, axis=None, dtype=None):
+        """Returns the cumulative product of the elements of the array along the given axis.
+
+Masked values are set to 1 internally.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+        """
+        result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
+        result.__setmask__(self.mask)
+        return result
+
+    def mean(self, axis=None, dtype=None):
+        """Averages the array over the given axis.  Equivalent to
+
+      a.sum(axis, dtype) / a.size(axis).
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+        """
+        if self._mask is nomask:
+            return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
+        else:
+            dsum = self.sum(axis=axis, dtype=dtype)
+            cnt = self.count(axis=axis)
+            return dsum*1./cnt
+
+    def anom(self, axis=None, dtype=None):
+        """Returns the anomalies (deviations from the average) along the given axis.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+            """
+        m = self.mean(axis, dtype)
+        if not axis:
+            return (self - m)
+        else:
+            return (self - expand_dims(m,axis))
+
+    def var(self, axis=None, dtype=None):
+        """Returns the variance, a measure of the spread of a distribution.
+
+The variance is the average of the squared deviations from the mean,
+i.e. var = mean((x - x.mean())**2).
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+
+*Notes*:
+    The value returned is a biased estimate of the true variance.
+    For the (more standard) unbiased estimate, use varu.
+        """
+        if self._mask is nomask:
+            # TODO: Do we keep super, or var _data and take a view ?
+            return super(MaskedArray, self).var(axis=axis, dtype=dtype)
+        else:
+            cnt = self.count(axis=axis)
+            danom = self.anom(axis=axis, dtype=dtype)
+            danom *= danom
+            dvar = narray(danom.sum(axis) / cnt).view(type(self))
+            if axis is not None:
+                dvar._mask = mask_or(self._mask.all(axis), (cnt==1))
+            dvar._update_from(self)
+            return dvar
+
+    def std(self, axis=None, dtype=None):
+        """Returns the standard deviation, a measure of the spread of a distribution.
+
+The standard deviation is the square root of the average of the squared
+deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation.
+        If not given, the current dtype is used instead.
+
+*Notes*:
+    The value returned is a biased estimate of the true standard deviation.
+    For the more standard unbiased estimate, use stdu.
+        """
+        dvar = self.var(axis,dtype)
+        if axis is not None or dvar is not masked:
+            dvar = sqrt(dvar)
+        return dvar
+
+    #............................................
+    def argsort(self, axis=None, fill_value=None, kind='quicksort',
+                order=None):
+        """Returns a ndarray of indices that sort the array along the specified axis.
+    Masked values are filled beforehand to fill_value.
+    Returns a numpy array.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis to be indirectly sorted.
+        If not given, uses a flatten version of the array.
+    fill_value : {var}
+        Value used to fill in the masked values.
+        If not given, self.fill_value is used instead.
+    kind : {string}
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+*Notes*:
+    This method executes an indirect sort along the given axis using the algorithm
+    specified by the kind keyword. It returns an array of indices of the same shape
+    as 'a' that index data along the given axis in sorted order.
+
+    The various sorts are characterized by average speed, worst case performance
+    need for work space, and whether they are stable.  A stable sort keeps items
+    with the same key in the same relative order. The three available algorithms
+    have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    All the sort algorithms make temporary copies of the data when the sort is not
+    along the last axis. Consequently, sorts along the last axis are faster and use
+    less space than sorts along other axis.
+        """
+        if fill_value is None:
+            fill_value = default_fill_value(self)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argsort(axis=axis, kind=kind, order=order)
+    #........................
+    def argmin(self, axis=None, fill_value=None):
+        """Returns a ndarray of indices for the minimum values of a along the
+specified axis.
+
+Masked values are treated as if they had the value fill_value.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, the output of minimum_fill_value(self._data) is used.
+        """
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argmin(axis)
+    #........................
+    def argmax(self, axis=None, fill_value=None):
+        """Returns the array of indices for the maximum values of `a` along the
+specified axis.
+
+Masked values are treated as if they had the value fill_value.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, the output of maximum_fill_value(self._data) is used.
+        """
+        if fill_value is None:
+            fill_value = maximum_fill_value(self._data)
+        d = self.filled(fill_value).view(ndarray)
+        return d.argmax(axis)
+
+    def sort(self, axis=-1, kind='quicksort', order=None,
+             endwith=True, fill_value=None):
+        """Sort a along the given axis.
+
+*Parameters*:
+    axis : {integer}
+        Axis to be indirectly sorted.
+    kind : {string}
+        Sorting algorithm (default 'quicksort')
+        Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+    order : {var}
+        If a has fields defined, then the order keyword can be the field
+        name to sort on or a list (or tuple) of field names to indicate
+        the order that fields should be used to define the sort.
+    fill_value : {var}
+        Value used to fill in the masked values.
+        If None, use the the output of minimum_fill_value().
+    endwith : {boolean}
+        Whether missing values (if any) should be forced in the upper indices
+        (at the end of the array) (True) or lower indices (at the beginning).
+
+*Returns*:
+    When used as method, returns None.
+    When used as a function, returns an array.
+
+*Notes*:
+    This method sorts 'a' in place along the given axis using the algorithm
+    specified by the kind keyword.
+
+    The various sorts may characterized by average speed, worst case performance
+    need for work space, and whether they are stable.  A stable sort keeps items
+    with the same key in the same relative order and is most useful when used w/
+    argsort where the key might differ from the items being sorted.
+    The three available algorithms have the following properties:
+
+    |------------------------------------------------------|
+    |    kind   | speed |  worst case | work space | stable|
+    |------------------------------------------------------|
+    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
+    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
+    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
+    |------------------------------------------------------|
+
+    """
+        if self._mask is nomask:
+            ndarray.sort(self,axis=axis, kind=kind, order=order)
+        else:
+            if fill_value is None:
+                if endwith:
+                    filler = minimum_fill_value(self)
+                else:
+                    filler = maximum_fill_value(self)
+            else:
+                filler = fill_value
+            idx = numpy.indices(self.shape)
+            idx[axis] = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
+            idx_l = idx.tolist()
+            tmp_mask = self._mask[idx_l].flat
+            tmp_data = self._data[idx_l].flat
+            self.flat = tmp_data
+            self._mask.flat = tmp_mask
+        return
+
+    #............................................
+    def min(self, axis=None, fill_value=None):
+        """Returns the minimum of a along the given axis.
+
+Masked values are filled with fill_value.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, use the the output of minimum_fill_value().
+    """
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).min(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Get the mask ................
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fil value ...........
+        if fill_value is None:
+            fill_value = minimum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).min(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def max(self, axis=None, fill_value=None):
+        """Returns the maximum/a along the given axis.
+
+Masked values are filled with fill_value.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, use the the output of maximum_fill_value().
+        """
+        mask = self._mask
+        # Check all/nothing case ......
+        if mask is nomask:
+            return super(MaskedArray, self).max(axis=axis)
+        elif (not mask.ndim) and mask:
+            return masked
+        # Check the mask ..............
+        if axis is None:
+            mask = umath.logical_and.reduce(mask.flat)
+        else:
+            mask = umath.logical_and.reduce(mask, axis=axis)
+        # Get the fill value ..........
+        if fill_value is None:
+            fill_value = maximum_fill_value(self)
+        # Get the data ................
+        result = self.filled(fill_value).max(axis=axis).view(type(self))
+        if result.ndim > 0:
+            result._mask = mask
+        return result
+    #........................
+    def ptp(self, axis=None, fill_value=None):
+        """Returns the visible data range (max-min) along the given axis.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, the maximum uses the maximum default, the minimum uses
+        the minimum default.
+        """
+        return self.max(axis, fill_value) - self.min(axis, fill_value)
+
+
+    # Array methods ---------------------------------------
+#    conj = conjugate = _arraymethod('conjugate')
+    copy = _arraymethod('copy')
+    diagonal = _arraymethod('diagonal')
+    take = _arraymethod('take')
+#    ravel = _arraymethod('ravel')
+    transpose = _arraymethod('transpose')
+    T = property(fget=lambda self:self.transpose())
+    swapaxes = _arraymethod('swapaxes')
+    clip = _arraymethod('clip', onmask=False)
+    compress = _arraymethod('compress')
+    copy = _arraymethod('copy')
+    squeeze = _arraymethod('squeeze')
+    #--------------------------------------------
+    def tolist(self, fill_value=None):
+        """Copies the data portion of the array to a hierarchical python list and
+returns that list.
+
+Data items are converted to the nearest compatible Python type.
+Masked values are converted to fill_value. If fill_value is None, the corresponding
+entries in the output list will be ``None``.
+    """
+        if fill_value is not None:
+            return self.filled(fill_value).tolist()
+        result = self.filled().tolist()
+        if self._mask is nomask:
+            return result
+        if self.ndim == 0:
+            return [None]
+        elif self.ndim == 1:
+            maskedidx = self._mask.nonzero()[0].tolist()
+            [operator.setitem(result,i,None) for i in maskedidx]
+        else:
+            for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
+                tmp = result
+                for i in idx[:-1]:
+                    tmp = tmp[i]
+                tmp[idx[-1]] = None
+        return result
+    #........................
+    def tostring(self, fill_value=None, order='C'):
+        """Returns a copy of array data as a Python string containing the raw
+bytes in the array.
+
+*Parameters*:
+    fill_value : {var}, optional
+        Value used to fill in the masked values.
+        If None, uses self.fill_value instead.
+    order : {string}
+        Order of the data item in the copy {"C","F","A"}.
+        "C"       -- C order (row major)
+        "Fortran" -- Fortran order (column major)
+        "Any"     -- Current order of array.
+        None      -- Same as "Any"
+    """
+        return self.filled(fill_value).tostring(order=order)
+    #--------------------------------------------
+    # Pickling
+    def __getstate__(self):
+        "Returns the internal state of the masked array, for pickling purposes."
+        state = (1,
+                 self.shape,
+                 self.dtype,
+                 self.flags.fnc,
+                 self._data.tostring(),
+                 getmaskarray(self).tostring(),
+                 self._fill_value,
+                 )
+        return state
+    #
+    def __setstate__(self, state):
+        """Restores the internal state of the masked array, for pickling purposes.
+``state`` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
+
+        - class name
+        - a tuple giving the shape of the data
+        - a typecode for the data
+        - a binary string for the data
+        - a binary string for the mask.
+            """
+        (ver, shp, typ, isf, raw, msk, flv) = state
+        ndarray.__setstate__(self, (shp, typ, isf, raw))
+        self._mask.__setstate__((shp, dtype(bool), isf, msk))
+        self.fill_value = flv
+    #
+    def __reduce__(self):
+        """Returns a 3-tuple for pickling a MaskedArray."""
+        return (_mareconstruct,
+                (self.__class__, self._baseclass, (0,), 'b', ),
+                self.__getstate__())
+
+
+def _mareconstruct(subtype, baseclass, baseshape, basetype,):
+    """Internal function that builds a new MaskedArray from the information stored
+in a pickle."""
+    _data = ndarray.__new__(baseclass, baseshape, basetype)
+    _mask = ndarray.__new__(ndarray, baseshape, 'b1')
+    return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype, shrink=False)
+#MaskedArray.__dump__ = dump
+#MaskedArray.__dumps__ = dumps
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Shortcuts ---
+#####---------------------------------------------------------------------------
+def isMaskedArray(x):
+    "Is x a masked array, that is, an instance of MaskedArray?"
+    return isinstance(x, MaskedArray)
+isarray = isMaskedArray
+isMA = isMaskedArray  #backward compatibility
+# We define the masked singleton as a float for higher precedence...
+masked_singleton = MaskedArray(0, dtype=float_, mask=True)
+masked = masked_singleton
+
+masked_array = MaskedArray
+
+def array(data, dtype=None, copy=False, order=False, mask=nomask, subok=True,
+          keep_mask=True, hard_mask=False, fill_value=None, shrink=True):
+    """array(data, dtype=None, copy=True, order=False, mask=nomask,
+             keep_mask=True, shrink=True, fill_value=None)
+Acts as shortcut to MaskedArray, with options in a different order for convenience.
+And backwards compatibility...
+    """
+    #TODO: we should try to put 'order' somwehere
+    return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, subok=subok,
+                       keep_mask=keep_mask, hard_mask=hard_mask,
+                       fill_value=fill_value)
+array.__doc__ = masked_array.__doc__
+
+def is_masked(x):
+    """Returns whether x has some masked values."""
+    m = getmask(x)
+    if m is nomask:
+        return False
+    elif m.any():
+        return True
+    return False
+
+
+#####---------------------------------------------------------------------------
+#---- --- Extrema functions ---
+#####---------------------------------------------------------------------------
+class _extrema_operation(object):
+    "Generic class for maximum/minimum functions."
+    def __call__(self, a, b=None):
+        "Executes the call behavior."
+        if b is None:
+            return self.reduce(a)
+        return where(self.compare(a, b), a, b)
+    #.........
+    def reduce(self, target, axis=None):
+        "Reduces target along the given axis."
+        m = getmask(target)
+        if axis is not None:
+            kargs = { 'axis' : axis }
+        else:
+            kargs = {}
+            target = target.ravel()
+            if not (m is nomask):
+                m = m.ravel()
+        if m is nomask:
+            t = self.ufunc.reduce(target, **kargs)
+        else:
+            target = target.filled(self.fill_value_func(target)).view(type(target))
+            t = self.ufunc.reduce(target, **kargs)
+            m = umath.logical_and.reduce(m, **kargs)
+            if hasattr(t, '_mask'):
+                t._mask = m
+            elif m:
+                t = masked
+        return t
+    #.........
+    def outer (self, a, b):
+        "Returns the function applied to the outer product of a and b."
+        ma = getmask(a)
+        mb = getmask(b)
+        if ma is nomask and mb is nomask:
+            m = nomask
+        else:
+            ma = getmaskarray(a)
+            mb = getmaskarray(b)
+            m = logical_or.outer(ma, mb)
+        result = self.ufunc.outer(filled(a), filled(b))
+        result._mask = m
+        return result
+
+#............................
+class _minimum_operation(_extrema_operation):
+    "Object to calculate minima"
+    def __init__ (self):
+        """minimum(a, b) or minimum(a)
+In one argument case, returns the scalar minimum.
+        """
+        self.ufunc = umath.minimum
+        self.afunc = amin
+        self.compare = less
+        self.fill_value_func = minimum_fill_value
+
+#............................
+class _maximum_operation(_extrema_operation):
+    "Object to calculate maxima"
+    def __init__ (self):
+        """maximum(a, b) or maximum(a)
+           In one argument case returns the scalar maximum.
+        """
+        self.ufunc = umath.maximum
+        self.afunc = amax
+        self.compare = greater
+        self.fill_value_func = maximum_fill_value
+
+#..........................................................
+def min(array, axis=None, out=None):
+    """Returns the minima along the given axis.
+If `axis` is None, applies to the flattened array."""
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return minimum(array)
+    else:
+        return minimum.reduce(array, axis)
+min.__doc__ = MaskedArray.min.__doc__
+#............................
+def max(obj, axis=None, out=None):
+    if out is not None:
+        raise TypeError("Output arrays Unsupported for masked arrays")
+    if axis is None:
+        return maximum(obj)
+    else:
+        return maximum.reduce(obj, axis)
+max.__doc__ = MaskedArray.max.__doc__
+#.............................
+def ptp(obj, axis=None):
+    """a.ptp(axis=None) =  a.max(axis)-a.min(axis)"""
+    try:
+        return obj.max(axis)-obj.min(axis)
+    except AttributeError:
+        return max(obj, axis=axis) - min(obj, axis=axis)
+ptp.__doc__ = MaskedArray.ptp.__doc__
+
+
+#####---------------------------------------------------------------------------
+#---- --- Definition of functions from the corresponding methods ---
+#####---------------------------------------------------------------------------
+class _frommethod:
+    """Defines functions from existing MaskedArray methods.
+:ivar _methodname (String): Name of the method to transform.
+    """
+    def __init__(self, methodname):
+        self._methodname = methodname
+        self.__doc__ = self.getdoc()
+    def getdoc(self):
+        "Returns the doc of the function (from the doc of the method)."
+        try:
+            return getattr(MaskedArray, self._methodname).__doc__
+        except:
+            return getattr(numpy, self._methodname).__doc__
+    def __call__(self, a, *args, **params):
+        if isinstance(a, MaskedArray):
+            return getattr(a, self._methodname).__call__(*args, **params)
+        #FIXME ----
+        #As x is not a MaskedArray, we transform it to a ndarray with asarray
+        #... and call the corresponding method.
+        #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
+        #we end up with a "SystemError: NULL result without error in PyObject_Call"
+        #A dirty trick is then to call the initial numpy function...
+        method = getattr(narray(a, copy=False), self._methodname)
+        try:
+            return method(*args, **params)
+        except SystemError:
+            return getattr(numpy,self._methodname).__call__(a, *args, **params)
+
+all = _frommethod('all')
+anomalies = anom = _frommethod('anom')
+any = _frommethod('any')
+conjugate = _frommethod('conjugate')
+ids = _frommethod('ids')
+nonzero = _frommethod('nonzero')
+diagonal = _frommethod('diagonal')
+maximum = _maximum_operation()
+mean = _frommethod('mean')
+minimum = _minimum_operation ()
+product = _frommethod('prod')
+ptp = _frommethod('ptp')
+ravel = _frommethod('ravel')
+repeat = _frommethod('repeat')
+std = _frommethod('std')
+sum = _frommethod('sum')
+swapaxes = _frommethod('swapaxes')
+take = _frommethod('take')
+var = _frommethod('var')
+
+#..............................................................................
+def power(a, b, third=None):
+    """Computes a**b elementwise.
+    Masked values are set to 1."""
+    if third is not None:
+        raise MAError, "3-argument power not supported."
+    ma = getmask(a)
+    mb = getmask(b)
+    m = mask_or(ma, mb)
+    fa = getdata(a)
+    fb = getdata(b)
+    if fb.dtype.char in typecodes["Integer"]:
+        return masked_array(umath.power(fa, fb), m)
+    md = make_mask((fa < 0), shrink=True)
+    m = mask_or(m, md)
+    if m is nomask:
+        return masked_array(umath.power(fa, fb))
+    else:
+        fa = fa.copy()
+        fa[(fa < 0)] = 1
+        return masked_array(umath.power(fa, fb), m)
+
+#..............................................................................
+def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None):
+    "Function version of the eponymous method."
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    if axis is None:
+        return d.argsort(kind=kind, order=order)
+    return d.argsort(axis, kind=kind, order=order)
+argsort.__doc__ = MaskedArray.argsort.__doc__
+
+def argmin(a, axis=None, fill_value=None):
+    "Function version of the eponymous method."
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+    d = filled(a, fill_value)
+    return d.argmin(axis=axis)
+argmin.__doc__ = MaskedArray.argmin.__doc__
+
+def argmax(a, axis=None, fill_value=None):
+    "Function version of the eponymous method."
+    if fill_value is None:
+        fill_value = default_fill_value(a)
+        try:
+            fill_value = - fill_value
+        except:
+            pass
+    d = filled(a, fill_value)
+    return d.argmax(axis=axis)
+argmin.__doc__ = MaskedArray.argmax.__doc__
+
+def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+    "Function version of the eponymous method."
+    a = narray(a, copy=False, subok=True)
+    if fill_value is None:
+        if endwith:
+            filler = minimum_fill_value(a)
+        else:
+            filler = maximum_fill_value(a)
+    else:
+        filler = fill_value
+#    return
+    indx = numpy.indices(a.shape).tolist()
+    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+    return a[indx]
+sort.__doc__ = MaskedArray.sort.__doc__
+
+
+def compressed(x):
+    """Returns a 1-D array of all the non-masked data."""
+    if getmask(x) is nomask:
+        return x
+    else:
+        return x.compressed()
+
+def concatenate(arrays, axis=0):
+    "Concatenates the arrays along the given axis."
+    d = numpy.concatenate([getdata(a) for a in arrays], axis)
+    rcls = get_masked_subclass(*arrays)
+    data = d.view(rcls)
+    # Check whether one of the arrays has a non-empty mask...
+    for x in arrays:
+        if getmask(x) is not nomask:
+            break
+        return data
+    # OK, so we have to concatenate the masks
+    dm = numpy.concatenate([getmaskarray(a) for a in arrays], axis)
+    shrink = numpy.logical_or.reduce([getattr(a,'_shrinkmask',True) for a in arrays])
+    if shrink and not dm.any():
+        data._mask = nomask
+    else:
+        data._mask = dm.reshape(d.shape)
+    return data
+
+def count(a, axis = None):
+    return masked_array(a, copy=False).count(axis)
+count.__doc__ = MaskedArray.count.__doc__
+
+
+def expand_dims(x,axis):
+    "Expands the shape of the array by including a new axis before the given one."
+    result = n_expand_dims(x,axis)
+    if isinstance(x, MaskedArray):
+        new_shape = result.shape
+        result = x.view()
+        result.shape = new_shape
+        if result._mask is not nomask:
+            result._mask.shape = new_shape
+    return result
+
+#......................................
+def left_shift (a, n):
+    "Left shift n bits."
+    m = getmask(a)
+    if m is nomask:
+        d = umath.left_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.left_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+
+def right_shift (a, n):
+    "Right shift n bits."
+    m = getmask(a)
+    if m is nomask:
+        d = umath.right_shift(filled(a), n)
+        return masked_array(d)
+    else:
+        d = umath.right_shift(filled(a, 0), n)
+        return masked_array(d, mask=m)
+
+#......................................
+def put(a, indices, values, mode='raise'):
+    """Sets storage-indexed locations to corresponding values.
+Values and indices are filled if necessary."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.put(indices, values, mode=mode)
+    except AttributeError:
+        return narray(a, copy=False).put(indices, values, mode=mode)
+
+def putmask(a, mask, values): #, mode='raise'):
+    """Sets a.flat[n] = values[n] for each n where mask.flat[n] is true.
+
+If values is not the same size of a and mask then it will repeat as necessary.
+This gives different behavior than a[mask] = values."""
+    # We can't use 'frommethod', the order of arguments is different
+    try:
+        return a.putmask(values, mask)
+    except AttributeError:
+        return numpy.putmask(narray(a, copy=False), mask, values)
+
+def transpose(a,axes=None):
+    """Returns a view of the array with dimensions permuted according to axes,
+as a masked array.
+
+If ``axes`` is None (default), the output view has reversed dimensions compared
+to the original.
+    """
+    #We can't use 'frommethod', as 'transpose' doesn't take keywords
+    try:
+        return a.transpose(axes)
+    except AttributeError:
+        return narray(a, copy=False).transpose(axes).view(MaskedArray)
+
+def reshape(a, new_shape):
+    """Changes the shape of the array a to new_shape."""
+    #We can't use 'frommethod', it whine about some parameters. Dmmit.
+    try:
+        return a.reshape(new_shape)
+    except AttributeError:
+        return narray(a, copy=False).reshape(new_shape).view(MaskedArray)
+
+def resize(x, new_shape):
+    """Returns a new array with the specified shape.
+
+The total size of the original array can be any size.
+The new array is filled with repeated copies of a. If a was masked, the new array
+will be masked, and the new mask will be a repetition of the old one.
+    """
+    # We can't use _frommethods here, as N.resize is notoriously whiny.
+    m = getmask(x)
+    if m is not nomask:
+        m = numpy.resize(m, new_shape)
+    result = numpy.resize(x, new_shape).view(get_masked_subclass(x))
+    if result.ndim:
+        result._mask = m
+    return result
+
+
+#................................................
+def rank(obj):
+    "maskedarray version of the numpy function."
+    return fromnumeric.rank(getdata(obj))
+rank.__doc__ = numpy.rank.__doc__
+#
+def shape(obj):
+    "maskedarray version of the numpy function."
+    return fromnumeric.shape(getdata(obj))
+shape.__doc__ = numpy.shape.__doc__
+#
+def size(obj, axis=None):
+    "maskedarray version of the numpy function."
+    return fromnumeric.size(getdata(obj), axis)
+size.__doc__ = numpy.size.__doc__
+#................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Extra functions ---
+#####--------------------------------------------------------------------------
+def where (condition, x=None, y=None):
+    """where(condition | x, y)
+
+Returns a (subclass of) masked array, shaped like condition, where the elements
+are x when condition is True, and  y otherwise.   If neither x nor y are given,
+returns a tuple of indices where condition is True (a la condition.nonzero()).
+
+*Parameters*:
+    condition : {var}
+        The condition to meet. Must be convertible to an integer array.
+    x : {var}, optional
+        Values of the output when the condition is met
+    y : {var}, optional
+        Values of the output when the condition is not met.
+    """
+    if x is None and y is None:
+        return filled(condition, 0).nonzero()
+    elif x is None or y is None:
+        raise ValueError, "Either both or neither x and y should be given."
+    # Get the condition ...............
+    fc = filled(condition, 0).astype(bool_)
+    notfc = numpy.logical_not(fc)
+    # Get the data ......................................
+    xv = getdata(x)
+    yv = getdata(y)
+    if x is masked:
+        ndtype = yv.dtype
+        xm = numpy.ones(fc.shape, dtype=MaskType)
+    elif y is masked:
+        ndtype = xv.dtype
+        ym = numpy.ones(fc.shape, dtype=MaskType)
+    else:
+        ndtype = numpy.max([xv.dtype, yv.dtype])
+        xm = getmask(x)
+    d = numpy.empty(fc.shape, dtype=ndtype).view(MaskedArray)
+    numpy.putmask(d._data, fc, xv.astype(ndtype))
+    numpy.putmask(d._data, notfc, yv.astype(ndtype))
+    d._mask = numpy.zeros(fc.shape, dtype=MaskType)
+    numpy.putmask(d._mask, fc, getmask(x))
+    numpy.putmask(d._mask, notfc, getmask(y))
+    d._mask |= getmaskarray(condition)
+    if not d._mask.any():
+        d._mask = nomask
+    return d
+#    # Get the data as a (subclass of) MaskedArray
+#    xv = getdata(x)
+#    yv = getdata(y)
+#    d = numpy.choose(fc, (yv, xv)).view(MaskedArray)
+#    # Get the mask ....................
+#    xm = getmask(x)
+#    ym = getmask(y)
+#    d.mask = numpy.choose(fc, (ym, xm)) | getmask(condition)
+#    # Fix the dtype if one of the values was masked, to prevent an upload to float
+#    if y is masked:
+#        ndtype = xv.dtype
+#    elif x is masked:
+#        ndtype = yv.dtype
+#    else:
+#        ndtype = d.dtype
+#    return d.astype(ndtype)
+
+def choose (indices, t, out=None, mode='raise'):
+    "Returns array shaped like indices with elements chosen from t"
+    #TODO: implement options `out` and `mode`, if possible.
+    def fmask (x):
+        "Returns the filled array, or True if masked."
+        if x is masked:
+            return 1
+        return filled(x)
+    def nmask (x):
+        "Returns the mask, True if ``masked``, False if ``nomask``."
+        if x is masked:
+            return 1
+        m = getmask(x)
+        if m is nomask:
+            return 0
+        return m
+    c = filled(indices, 0)
+    masks = [nmask(x) for x in t]
+    a = [fmask(x) for x in t]
+    d = numpy.choose(c, a)
+    m = numpy.choose(c, masks)
+    m = make_mask(mask_or(m, getmask(indices)), copy=0, shrink=True)
+    return masked_array(d, mask=m)
+
+def round_(a, decimals=0, out=None):
+    """Returns a copy of a, rounded to 'decimals' places.
+
+When 'decimals' is negative, it specifies the number of positions to the left of
+the decimal point.  The real and imaginary parts of complex numbers are rounded
+separately. Nothing is done if the array is not of float type and 'decimals' is
+greater than or equal to 0.
+
+*Parameters*:
+    decimals : {integer}
+        Number of decimals to round to. May be negative.
+    out : {ndarray}
+        Existing array to use for output.
+        If not given, returns a default copy of a.
+
+*Notes*:
+    If out is given and does not have a mask attribute, the mask of a is lost!
+    """
+    if out is None:
+        result = fromnumeric.round_(getdata(a), decimals, out)
+        if isinstance(a,MaskedArray):
+            result = result.view(type(a))
+            result._mask = a._mask
+        else:
+            result = result.view(MaskedArray)
+        return result
+    else:
+        fromnumeric.round_(getdata(a), decimals, out)
+        if hasattr(out, '_mask'):
+            out._mask = getmask(a)
+        return out
+
+def arange(stop, start=None, step=1, dtype=None):
+    "maskedarray version of the numpy function."
+    return numpy.arange(stop, start, step, dtype).view(MaskedArray)
+arange.__doc__ = numpy.arange.__doc__
+
+def inner(a, b):
+    "maskedarray version of the numpy function."
+    fa = filled(a, 0)
+    fb = filled(b, 0)
+    if len(fa.shape) == 0:
+        fa.shape = (1,)
+    if len(fb.shape) == 0:
+        fb.shape = (1,)
+    return numpy.inner(fa, fb).view(MaskedArray)
+inner.__doc__ = numpy.inner.__doc__
+inner.__doc__ += "\n*Notes*:\n    Masked values are replaced by 0."
+innerproduct = inner
+
+def outer(a, b):
+    "maskedarray version of the numpy function."
+    fa = filled(a, 0).ravel()
+    fb = filled(b, 0).ravel()
+    d = numeric.outer(fa, fb)
+    ma = getmask(a)
+    mb = getmask(b)
+    if ma is nomask and mb is nomask:
+        return masked_array(d)
+    ma = getmaskarray(a)
+    mb = getmaskarray(b)
+    m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
+    return masked_array(d, mask=m)
+outer.__doc__ = numpy.outer.__doc__
+outer.__doc__ += "\n*Notes*:\n    Masked values are replaced by 0."
+outerproduct = outer
+
+def allequal (a, b, fill_value=True):
+    """Returns True if all entries of  a and b are equal, using fill_value
+as a truth value where either or both are masked.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    if m is nomask:
+        x = getdata(a)
+        y = getdata(b)
+        d = umath.equal(x, y)
+        return d.all()
+    elif fill_value:
+        x = getdata(a)
+        y = getdata(b)
+        d = umath.equal(x, y)
+        dm = array(d, mask=m, copy=False)
+        return dm.filled(True).all(None)
+    else:
+        return False
+
+def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+    """ Returns True if all elements of a and b are equal subject to given tolerances.
+If fill_value is True, masked values are considered equal.
+If fill_value is False, masked values considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error atol comes into play for those elements of b that are very small
+or zero; it says how small `a` must be also.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    d1 = getdata(a)
+    d2 = getdata(b)
+    x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
+    y = filled(array(d2, copy=0, mask=m), 1).astype(float)
+    d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+    return fromnumeric.alltrue(fromnumeric.ravel(d))
+
+#..............................................................................
+def asarray(a, dtype=None):
+    """asarray(data, dtype) = array(data, dtype, copy=0, subok=0)
+Returns a as a MaskedArray object of the given dtype.
+If dtype is not given or None, is is set to the dtype of a.
+No copy is performed if a is already an array.
+Subclasses are converted to the base class MaskedArray.
+    """
+    return masked_array(a, dtype=dtype, copy=False, keep_mask=True, subok=False)
+
+def asanyarray(a, dtype=None):
+    """asanyarray(data, dtype) = array(data, dtype, copy=0, subok=1)
+Returns a as an masked array.
+If dtype is not given or None, is is set to the dtype of a.
+No copy is performed if a is already an array.
+Subclasses are conserved.
+    """
+    return masked_array(a, dtype=dtype, copy=False, keep_mask=True, subok=True)
+
+
+def empty(new_shape, dtype=float):
+    "maskedarray version of the numpy function."
+    return numpy.empty(new_shape, dtype).view(MaskedArray)
+empty.__doc__ = numpy.empty.__doc__
+
+def empty_like(a):
+    "maskedarray version of the numpy function."
+    return numpy.empty_like(a).view(MaskedArray)
+empty_like.__doc__ = numpy.empty_like.__doc__
+
+def ones(new_shape, dtype=float):
+    "maskedarray version of the numpy function."
+    return numpy.ones(new_shape, dtype).view(MaskedArray)
+ones.__doc__ = numpy.ones.__doc__
+
+def zeros(new_shape, dtype=float):
+    "maskedarray version of the numpy function."
+    return numpy.zeros(new_shape, dtype).view(MaskedArray)
+zeros.__doc__ = numpy.zeros.__doc__
+
+#####--------------------------------------------------------------------------
+#---- --- Pickling ---
+#####--------------------------------------------------------------------------
+def dump(a,F):
+    """Pickles the MaskedArray `a` to the file `F`.
+`F` can either be the handle of an exiting file, or a string representing a file name.
+    """
+    if not hasattr(F,'readline'):
+        F = open(F,'w')
+    return cPickle.dump(a,F)
+
+def dumps(a):
+    """Returns a string corresponding to the pickling of the MaskedArray."""
+    return cPickle.dumps(a)
+
+def load(F):
+    """Wrapper around ``cPickle.load`` which accepts either a file-like object or
+ a filename."""
+    if not hasattr(F, 'readline'):
+        F = open(F,'r')
+    return cPickle.load(F)
+
+def loads(strg):
+    "Loads a pickle from the current string."""
+    return cPickle.loads(strg)
+
+
+###############################################################################
+
+if __name__ == '__main__':
+    from maskedarray.testutils import assert_equal, assert_almost_equal
+
+    # Small arrays ..................................
+    xs = numpy.random.uniform(-1,1,6).reshape(2,3)
+    ys = numpy.random.uniform(-1,1,6).reshape(2,3)
+    zs = xs + 1j * ys
+    m1 = [[True, False, False], [False, False, True]]
+    m2 = [[True, False, True], [False, False, True]]
+    nmxs = numpy.ma.array(xs, mask=m1)
+    nmys = numpy.ma.array(ys, mask=m2)
+    nmzs = numpy.ma.array(zs, mask=m1)
+    mmxs = array(xs, mask=m1)
+    mmys = array(ys, mask=m2)
+    mmzs = array(zs, mask=m1)
+    # Big arrays ....................................
+    xl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+    yl = numpy.random.uniform(-1,1,100*100).reshape(100,100)
+    zl = xl + 1j * yl
+    maskx = xl > 0.8
+    masky = yl < -0.8
+    nmxl = numpy.ma.array(xl, mask=maskx)
+    nmyl = numpy.ma.array(yl, mask=masky)
+    nmzl = numpy.ma.array(zl, mask=maskx)
+    mmxl = array(xl, mask=maskx, shrink=True)
+    mmyl = array(yl, mask=masky, shrink=True)
+    mmzl = array(zl, mask=maskx, shrink=True)
+    #
+    z = empty(3,)
+    mmys.all(0, out=z)
+
+    if 1:
+        x = numpy.array([[ 0.13,  0.26,  0.90],
+                     [ 0.28,  0.33,  0.63],
+                     [ 0.31,  0.87,  0.70]])
+        m = numpy.array([[ True, False, False],
+                     [False, False, False],
+                     [True,  True, False]], dtype=numpy.bool_)
+        mx = masked_array(x, mask=m)
+        xbig = numpy.array([[False, False,  True],
+                        [False, False,  True],
+                        [False,  True,  True]], dtype=numpy.bool_)
+        mxbig = (mx > 0.5)
+        mxsmall = (mx < 0.5)
+        #
+        assert (mxbig.all()==False)
+        assert (mxbig.any()==True)
+        assert_equal(mxbig.all(0),[False, False, True])
+        assert_equal(mxbig.all(1), [False, False, True])
+        assert_equal(mxbig.any(0),[False, False, True])
+        assert_equal(mxbig.any(1), [True, True, True])
+
+    if 1:
+        xx = array([1+10j,20+2j], mask=[1,0])
+        assert_equal(xx.imag,[10,2])
+        assert_equal(xx.imag.filled(), [1e+20,2])
+        assert_equal(xx.real,[1,20])
+        assert_equal(xx.real.filled(), [1e+20,20])

Added: trunk/scipy/sandbox/maskedarray/extras.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/extras.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/extras.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,722 @@
+"""Masked arrays add-ons.
+
+A collection of utilities for maskedarray
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: extras.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+__all__ = [
+'apply_along_axis', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'average',
+'vstack', 'hstack', 'dstack', 'row_stack', 'column_stack',
+'compress_rowcols', 'compress_rows', 'compress_cols', 'count_masked',
+'dot', 'hsplit',
+'mask_rowcols','mask_rows','mask_cols','masked_all','masked_all_like',
+'mediff1d', 'mr_',
+'notmasked_edges','notmasked_contiguous',
+'stdu', 'varu',
+           ]
+
+from itertools import groupby
+
+import core
+from core import *
+
+import numpy
+from numpy import float_
+import numpy.core.umath as umath
+import numpy.core.numeric as numeric
+from numpy.core.numeric import ndarray
+from numpy.core.numeric import array as nxarray
+from numpy.core.fromnumeric import asarray as nxasarray
+
+from numpy.lib.index_tricks import AxisConcatenator
+import numpy.lib.function_base as function_base
+
+#...............................................................................
+def issequence(seq):
+    """Returns True if the argument is a sequence (ndarray, list or tuple)."""
+    if isinstance(seq, ndarray):
+        return True
+    elif isinstance(seq, tuple):
+        return True
+    elif isinstance(seq, list):
+        return True
+    return False
+
+def count_masked(arr, axis=None):
+    """Counts the number of masked elements along the given axis.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to count.
+        If None (default), a flattened version of the array is used.
+    """
+    m = getmaskarray(arr)
+    return m.sum(axis)
+
+def masked_all(shape, dtype=float_):
+    """Returns an empty masked array of the given shape and dtype,
+    where all the data are masked.
+
+*Parameters*:
+    dtype : {dtype}, optional
+        Data type of the output.
+    """
+    a = masked_array(numeric.empty(shape, dtype),
+                     mask=numeric.ones(shape, bool_))
+    return a
+
+def masked_all_like(arr):
+    """Returns an empty masked array of the same shape and dtype as the array `a`,
+    where all the data are masked."""
+    a = masked_array(numeric.empty_like(arr),
+                     mask=numeric.ones(arr.shape, bool_))
+    return a
+
+#####--------------------------------------------------------------------------
+#---- --- New methods ---
+#####--------------------------------------------------------------------------
+def varu(a, axis=None, dtype=None):
+    """Returns an unbiased estimate of the variance.
+    i.e. var = sum((x - x.mean())**2)/(size(x,axis)-1)
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation. If not given, the current dtype
+        is used instead.
+
+*Notes*:
+    The value returned is an unbiased estimate of the true variance.
+    For the (less standard) biased estimate, use var.
+        """
+    a = asarray(a)
+    cnt = a.count(axis=axis)
+    anom = a.anom(axis=axis, dtype=dtype)
+    anom *= anom
+    dvar = anom.sum(axis) / (cnt-1)
+    if axis is None:
+        return dvar
+    dvar.__setmask__(mask_or(a._mask.all(axis), (cnt==1)))
+    return dvar
+#    return a.__class__(dvar,
+#                          mask=mask_or(a._mask.all(axis), (cnt==1)),
+#                          fill_value=a._fill_value)
+
+def stdu(a, axis=None, dtype=None):
+    """Returns an unbiased estimate of the standard deviation.
+    The standard deviation is the square root of the average of the squared
+    deviations from the mean, i.e. stdu = sqrt(varu(x)).
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    dtype : {dtype}, optional
+        Datatype for the intermediary computation.
+        If not given, the current dtype is used instead.
+
+*Notes*:
+    The value returned is an unbiased estimate of the true standard deviation.
+    For the (less standard) biased estimate, use std.
+        """
+    a = asarray(a)
+    dvar = a.varu(axis,dtype)
+    if axis is None:
+        if dvar is masked:
+            return masked
+        else:
+            # Should we use umath.sqrt instead ?
+            return sqrt(dvar)
+    return sqrt(dvar)
+#    return a.__class__(sqrt(dvar._data), mask=dvar._mask,
+#                          fill_value=a._fill_value)
+
+MaskedArray.stdu = stdu
+MaskedArray.varu = varu
+
+#####--------------------------------------------------------------------------
+#---- --- Standard functions ---
+#####--------------------------------------------------------------------------
+class _fromnxfunction:
+    """Defines a wrapper to adapt numpy functions to masked arrays."""
+    def __init__(self, funcname):
+        self._function = funcname
+        self.__doc__ = self.getdoc()
+    def getdoc(self):
+        "Retrieves the __doc__ string from the function."
+        return getattr(numpy, self._function).__doc__ +\
+            "*Notes*:\n    (The function is applied to both the _data and the _mask, if any.)"
+    def __call__(self, *args, **params):
+        func = getattr(numpy, self._function)
+        if len(args)==1:
+            x = args[0]
+            if isinstance(x,ndarray):
+                _d = func(nxasarray(x), **params)
+                _m = func(getmaskarray(x), **params)
+                return masked_array(_d, mask=_m)
+            elif isinstance(x, tuple) or isinstance(x, list):
+                _d = func(tuple([nxasarray(a) for a in x]), **params)
+                _m = func(tuple([getmaskarray(a) for a in x]), **params)
+                return masked_array(_d, mask=_m)
+        else:
+            arrays = []
+            args = list(args)
+            while len(args)>0 and issequence(args[0]):
+                arrays.append(args.pop(0))
+            res = []
+            for x in arrays:
+                _d = func(nxasarray(x), *args, **params)
+                _m = func(getmaskarray(x), *args, **params)
+                res.append(masked_array(_d, mask=_m))
+            return res
+
+atleast_1d = _fromnxfunction('atleast_1d')
+atleast_2d = _fromnxfunction('atleast_2d')
+atleast_3d = _fromnxfunction('atleast_3d')
+
+vstack = row_stack = _fromnxfunction('vstack')
+hstack = _fromnxfunction('hstack')
+column_stack = _fromnxfunction('column_stack')
+dstack = _fromnxfunction('dstack')
+
+hsplit = _fromnxfunction('hsplit')
+
+#####--------------------------------------------------------------------------
+#----
+#####--------------------------------------------------------------------------
+def flatten_inplace(seq):
+    """Flattens a sequence in place."""
+    k = 0
+    while (k != len(seq)):
+        while hasattr(seq[k],'__iter__'):
+            seq[k:(k+1)] = seq[k]
+        k += 1
+    return seq
+
+
+def apply_along_axis(func1d,axis,arr,*args,**kwargs):
+    """ Execute func1d(arr[i],*args) where func1d takes 1-D arrays
+        and arr is an N-d array.  i varies so as to apply the function
+        along the given axis for each 1-d subarray in arr.
+    """
+    arr = core.array(arr, copy=False, subok=True)
+    nd = arr.ndim
+    if axis < 0:
+        axis += nd
+    if (axis >= nd):
+        raise ValueError("axis must be less than arr.ndim; axis=%d, rank=%d."
+            % (axis,nd))
+    ind = [0]*(nd-1)
+    i = numeric.zeros(nd,'O')
+    indlist = range(nd)
+    indlist.remove(axis)
+    i[axis] = slice(None,None)
+    outshape = numeric.asarray(arr.shape).take(indlist)
+    i.put(indlist, ind)
+    j = i.copy()
+    res = func1d(arr[tuple(i.tolist())],*args,**kwargs)
+    #  if res is a number, then we have a smaller output array
+    asscalar = numeric.isscalar(res)
+    if not asscalar:
+        try:
+            len(res)
+        except TypeError:
+            asscalar = True
+    # Note: we shouldn't set the dtype of the output from the first result...
+    #...so we force the type to object, and build a list of dtypes
+    #...we'll just take the largest, to avoid some downcasting
+    dtypes = []
+    if asscalar:
+        dtypes.append(numeric.asarray(res).dtype)
+        outarr = zeros(outshape, object_)
+        outarr[tuple(ind)] = res
+        Ntot = numeric.product(outshape)
+        k = 1
+        while k < Ntot:
+            # increment the index
+            ind[-1] += 1
+            n = -1
+            while (ind[n] >= outshape[n]) and (n > (1-nd)):
+                ind[n-1] += 1
+                ind[n] = 0
+                n -= 1
+            i.put(indlist,ind)
+            res = func1d(arr[tuple(i.tolist())],*args,**kwargs)
+            outarr[tuple(ind)] = res
+            dtypes.append(asarray(res).dtype)
+            k += 1
+    else:
+        res = core.array(res, copy=False, subok=True)
+        j = i.copy()
+        j[axis] = ([slice(None,None)] * res.ndim)
+        j.put(indlist, ind)
+        Ntot = numeric.product(outshape)
+        holdshape = outshape
+        outshape = list(arr.shape)
+        outshape[axis] = res.shape
+        dtypes.append(asarray(res).dtype)
+        outshape = flatten_inplace(outshape)
+        outarr = zeros(outshape, object_)
+        outarr[tuple(flatten_inplace(j.tolist()))] = res
+        k = 1
+        while k < Ntot:
+            # increment the index
+            ind[-1] += 1
+            n = -1
+            while (ind[n] >= holdshape[n]) and (n > (1-nd)):
+                ind[n-1] += 1
+                ind[n] = 0
+                n -= 1
+            i.put(indlist, ind)
+            j.put(indlist, ind)
+            res = func1d(arr[tuple(i.tolist())],*args,**kwargs)
+            outarr[tuple(flatten_inplace(j.tolist()))] = res
+            dtypes.append(asarray(res).dtype)
+            k += 1
+    max_dtypes = numeric.dtype(numeric.asarray(dtypes).max())
+    if not hasattr(arr, '_mask'):
+        result = numeric.asarray(outarr, dtype=max_dtypes)
+    else:
+        result = core.asarray(outarr, dtype=max_dtypes)
+        result.fill_value = core.default_fill_value(result)
+    return result
+
+def average (a, axis=None, weights=None, returned=False):
+    """Averages the array over the given axis.
+
+*Parameters*:
+    axis : {integer}, optional
+        Axis along which to perform the operation.
+        If None, applies to a flattened version of the array.
+    weights : {sequence}, optional
+        Sequence of weights.
+        The weights must have the shape of a, or be 1D with length the size of a
+        along the given axis.
+        If no weights are given, weights are assumed to be 1.
+    returned : {boolean}
+        Flag indicating whether a tuple (result, sum of weights/counts) should be
+        returned as output (True), or just the result (False).
+    """
+    a = asarray(a)
+    mask = a.mask
+    ash = a.shape
+    if ash == ():
+        ash = (1,)
+    if axis is None:
+        if mask is nomask:
+            if weights is None:
+                n = a.sum(axis=None)
+                d = float(a.size)
+            else:
+                w = filled(weights, 0.0).ravel()
+                n = umath.add.reduce(a._data.ravel() * w)
+                d = umath.add.reduce(w)
+                del w
+        else:
+            if weights is None:
+                n = a.filled(0).sum(axis=None)
+                d = umath.add.reduce((-mask).ravel().astype(int_))
+            else:
+                w = array(filled(weights, 0.0), float, mask=mask).ravel()
+                n = add.reduce(a.ravel() * w)
+                d = add.reduce(w)
+                del w
+    else:
+        if mask is nomask:
+            if weights is None:
+                d = ash[axis] * 1.0
+                n = add.reduce(a._data, axis, dtype=float_)
+            else:
+                w = filled(weights, 0.0)
+                wsh = w.shape
+                if wsh == ():
+                    wsh = (1,)
+                if wsh == ash:
+                    w = numeric.array(w, float_, copy=0)
+                    n = add.reduce(a*w, axis)
+                    d = add.reduce(w, axis)
+                    del w
+                elif wsh == (ash[axis],):
+                    ni = ash[axis]
+                    r = [None]*len(ash)
+                    r[axis] = slice(None, None, 1)
+                    w = eval ("w["+ repr(tuple(r)) + "] * ones(ash, float)")
+                    n = add.reduce(a*w, axis, dtype=float_)
+                    d = add.reduce(w, axis, dtype=float_)
+                    del w, r
+                else:
+                    raise ValueError, 'average: weights wrong shape.'
+        else:
+            if weights is None:
+                n = add.reduce(a, axis, dtype=float_)
+                d = umath.add.reduce((-mask), axis=axis, dtype=float_)
+            else:
+                w = filled(weights, 0.0)
+                wsh = w.shape
+                if wsh == ():
+                    wsh = (1,)
+                if wsh == ash:
+                    w = array(w, dtype=float_, mask=mask, copy=0)
+                    n = add.reduce(a*w, axis, dtype=float_)
+                    d = add.reduce(w, axis, dtype=float_)
+                elif wsh == (ash[axis],):
+                    ni = ash[axis]
+                    r = [None]*len(ash)
+                    r[axis] = slice(None, None, 1)
+                    w = eval ("w["+ repr(tuple(r)) + "] * masked_array(ones(ash, float), mask)")
+                    n = add.reduce(a*w, axis, dtype=float_)
+                    d = add.reduce(w, axis, dtype=float_)
+                else:
+                    raise ValueError, 'average: weights wrong shape.'
+                del w
+    if n is masked or d is masked:
+        return masked
+    result = n/d
+    del n
+
+    if isMaskedArray(result):
+        if ((axis is None) or (axis==0 and a.ndim == 1)) and \
+           (result.mask is nomask):
+            result = result._data
+        if returned:
+            if not isMaskedArray(d):
+                d = masked_array(d)
+            if isinstance(d, ndarray) and (not d.shape == result.shape):
+                d = ones(result.shape, dtype=float_) * d
+    if returned:
+        return result, d
+    else:
+        return result
+
+#..............................................................................
+def compress_rowcols(x, axis=None):
+    """Suppresses the rows and/or columns of a 2D array that contains masked values.
+
+    The suppression behavior is selected with the `axis`parameter.
+        - If axis is None, rows and columns are suppressed.
+        - If axis is 0, only rows are suppressed.
+        - If axis is 1 or -1, only columns are suppressed.
+
+*Returns*:
+    compressed_array : a ndarray.
+    """
+    x = asarray(x)
+    if x.ndim != 2:
+        raise NotImplementedError, "compress2d works for 2D arrays only."
+    m = getmask(x)
+    # Nothing is masked: return x
+    if m is nomask or not m.any():
+        return x._data
+    # All is masked: return empty
+    if m.all():
+        return nxarray([])
+    # Builds a list of rows/columns indices
+    (idxr, idxc) = (range(len(x)), range(x.shape[1]))
+    masked = m.nonzero()
+    if not axis:
+        for i in function_base.unique(masked[0]):
+            idxr.remove(i)
+    if axis in [None, 1, -1]:
+        for j in function_base.unique(masked[1]):
+            idxc.remove(j)
+    return x._data[idxr][:,idxc]
+
+def compress_rows(a):
+    """Suppresses whole rows of a 2D array that contain masked values."""
+    return compress_rowcols(a,0)
+
+def compress_cols(a):
+    """Suppresses whole columnss of a 2D array that contain masked values."""
+    return compress_rowcols(a,1)
+
+def mask_rowcols(a, axis=None):
+    """Masks whole rows and/or columns of a 2D array that contain masked values.
+    The masking behavior is selected with the `axis`parameter.
+        - If axis is None, rows and columns are masked.
+        - If axis is 0, only rows are masked.
+        - If axis is 1 or -1, only columns are masked.
+    Returns a *pure* ndarray.
+    """
+    a = asarray(a)
+    if a.ndim != 2:
+        raise NotImplementedError, "compress2d works for 2D arrays only."
+    m = getmask(a)
+    # Nothing is masked: return a
+    if m is nomask or not m.any():
+        return a
+    maskedval = m.nonzero()
+    a._mask = a._mask.copy()
+    if not axis:
+        a[function_base.unique(maskedval[0])] = masked
+    if axis in [None, 1, -1]:
+        a[:,function_base.unique(maskedval[1])] = masked
+    return a
+
+def mask_rows(a, axis=None):
+    """Masks whole rows of a 2D array that contain masked values."""
+    return mask_rowcols(a, 0)
+
+def mask_cols(a, axis=None):
+    """Masks whole columns of a 2D array that contain masked values."""
+    return mask_rowcols(a, 1)
+
+
+def dot(a,b, strict=False):
+    """Returns the dot product of two 2D masked arrays a and b.
+
+    Like the generic numpy equivalent, the product sum is over the last dimension
+    of a and the second-to-last dimension of b.
+    If strict is True, masked values are propagated: if a masked value appears
+    in a row or column, the whole row or column is considered masked.
+
+*Parameters*:
+    strict : {boolean}
+        Whether masked data are propagated (True) or set to 0 for the computation.
+
+*Note*:
+    The first argument is not conjugated.
+    """
+    #TODO: Works only with 2D arrays. There should be a way to get it to run with higher dimension
+    if strict and (a.ndim == 2) and (b.ndim == 2):
+        a = mask_rows(a)
+        b = mask_cols(b)
+    #
+    d = numpy.dot(filled(a, 0), filled(b, 0))
+    #
+    am = (~getmaskarray(a))
+    bm = (~getmaskarray(b))
+    m = ~numpy.dot(am,bm)
+    return masked_array(d, mask=m)
+
+#...............................................................................
+def mediff1d(array, to_end=None, to_begin=None):
+    """Returns the differences between consecutive elements of an array, possibly with
+    prefixed and/or appended values.
+
+*Parameters*:
+    array : {array}
+        Input array,  will be flattened before the difference is taken.
+    to_end : {number}, optional
+        If provided, this number will be tacked onto the end of the returned
+        differences.
+    to_begin : {number}, optional
+        If provided, this number will be taked onto the beginning of the
+        returned differences.
+
+*Returns*:
+      ed : {array}
+        The differences. Loosely, this will be (ary[1:] - ary[:-1]).
+    """
+    a = masked_array(array, copy=True)
+    if a.ndim > 1:
+        a.reshape((a.size,))
+    (d, m, n) = (a._data, a._mask, a.size-1)
+    dd = d[1:]-d[:-1]
+    if m is nomask:
+        dm = nomask
+    else:
+        dm = m[1:]-m[:-1]
+    #
+    if to_end is not None:
+        to_end = asarray(to_end)
+        nend = to_end.size
+        if to_begin is not None:
+            to_begin = asarray(to_begin)
+            nbegin = to_begin.size
+            r_data = numeric.empty((n+nend+nbegin,), dtype=a.dtype)
+            r_mask = numeric.zeros((n+nend+nbegin,), dtype=bool_)
+            r_data[:nbegin] = to_begin._data
+            r_mask[:nbegin] = to_begin._mask
+            r_data[nbegin:-nend] = dd
+            r_mask[nbegin:-nend] = dm
+        else:
+            r_data = numeric.empty((n+nend,), dtype=a.dtype)
+            r_mask = numeric.zeros((n+nend,), dtype=bool_)
+            r_data[:-nend] = dd
+            r_mask[:-nend] = dm
+        r_data[-nend:] = to_end._data
+        r_mask[-nend:] = to_end._mask
+    #
+    elif to_begin is not None:
+        to_begin = asarray(to_begin)
+        nbegin = to_begin.size
+        r_data = numeric.empty((n+nbegin,), dtype=a.dtype)
+        r_mask = numeric.zeros((n+nbegin,), dtype=bool_)
+        r_data[:nbegin] = to_begin._data
+        r_mask[:nbegin] = to_begin._mask
+        r_data[nbegin:] = dd
+        r_mask[nbegin:] = dm
+    #
+    else:
+        r_data = dd
+        r_mask = dm
+    return masked_array(r_data, mask=r_mask)
+
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Concatenation helpers ---
+#####--------------------------------------------------------------------------
+
+class mconcatenator(AxisConcatenator):
+    """Translates slice objects to concatenation along an axis."""
+
+    def __init__(self, axis=0):
+        AxisConcatenator.__init__(self, axis, matrix=False)
+
+    def __getitem__(self,key):
+        if isinstance(key, str):
+            raise MAError, "Unavailable for masked array."
+        if type(key) is not tuple:
+            key = (key,)
+        objs = []
+        scalars = []
+        final_dtypedescr = None
+        for k in range(len(key)):
+            scalar = False
+            if type(key[k]) is slice:
+                step = key[k].step
+                start = key[k].start
+                stop = key[k].stop
+                if start is None:
+                    start = 0
+                if step is None:
+                    step = 1
+                if type(step) is type(1j):
+                    size = int(abs(step))
+                    newobj = function_base.linspace(start, stop, num=size)
+                else:
+                    newobj = numeric.arange(start, stop, step)
+            elif type(key[k]) is str:
+                if (key[k] in 'rc'):
+                    self.matrix = True
+                    self.col = (key[k] == 'c')
+                    continue
+                try:
+                    self.axis = int(key[k])
+                    continue
+                except (ValueError, TypeError):
+                    raise ValueError, "Unknown special directive"
+            elif type(key[k]) in numeric.ScalarType:
+                newobj = asarray([key[k]])
+                scalars.append(k)
+                scalar = True
+            else:
+                newobj = key[k]
+            objs.append(newobj)
+            if isinstance(newobj, numeric.ndarray) and not scalar:
+                if final_dtypedescr is None:
+                    final_dtypedescr = newobj.dtype
+                elif newobj.dtype > final_dtypedescr:
+                    final_dtypedescr = newobj.dtype
+        if final_dtypedescr is not None:
+            for k in scalars:
+                objs[k] = objs[k].astype(final_dtypedescr)
+        res = concatenate(tuple(objs),axis=self.axis)
+        return self._retval(res)
+
+class mr_class(mconcatenator):
+    """Translates slice objects to concatenation along the first axis.
+
+        For example:
+        >>> mr_[array([1,2,3]), 0, 0, array([4,5,6])]
+        array([1, 2, 3, 0, 0, 4, 5, 6])
+    """
+    def __init__(self):
+        mconcatenator.__init__(self, 0)
+
+mr_ = mr_class()
+
+#####--------------------------------------------------------------------------
+#---- ---
+#####--------------------------------------------------------------------------
+
+def flatnotmasked_edges(a):
+    """Finds the indices of the first and last not masked values in a 1D masked array.
+    If all values are masked, returns None.
+    """
+    m = getmask(a)
+    if m is nomask or not numpy.any(m):
+        return [0,-1]
+    unmasked = numeric.flatnonzero(~m)
+    if len(unmasked) > 0:
+        return unmasked[[0,-1]]
+    else:
+        return None
+
+def notmasked_edges(a, axis=None):
+    """Finds the indices of the first and last not masked values along the given
+    axis in a masked array.
+    If all values are masked, returns None.
+    Otherwise, returns a list of 2 tuples, corresponding to the indices of the
+    first and last unmasked values respectively.
+    """
+    a = asarray(a)
+    if axis is None or a.ndim == 1:
+        return flatnotmasked_edges(a)
+    m = getmask(a)
+    idx = array(numpy.indices(a.shape), mask=nxasarray([m]*a.ndim))
+    return [tuple([idx[i].min(axis).compressed() for i in range(a.ndim)]),
+            tuple([idx[i].max(axis).compressed() for i in range(a.ndim)]),]
+
+def flatnotmasked_contiguous(a):
+    """Finds contiguous unmasked data in a flattened masked array.
+    Returns a sorted sequence of slices (start index, end index).
+    """
+    m = getmask(a)
+    if m is nomask:
+        return (a.size, [0,-1])
+    unmasked = numeric.flatnonzero(~m)
+    if len(unmasked) == 0:
+        return None
+    result = []
+    for k, group in groupby(enumerate(unmasked), lambda (i,x):i-x):
+        tmp = numpy.fromiter((g[1] for g in group), int_)
+#        result.append((tmp.size, tuple(tmp[[0,-1]])))
+        result.append( slice(tmp[0],tmp[-1]) )
+    result.sort()
+    return result
+
+def notmasked_contiguous(a, axis=None):
+    """Finds contiguous unmasked data in a masked array along the given axis.
+    Returns a sorted sequence of slices (start index, end index).
+    Note: Only accepts 2D arrays at most.
+    """
+    a = asarray(a)
+    nd = a.ndim
+    if nd > 2:
+        raise NotImplementedError,"Currently limited to atmost 2D array."
+    if axis is None or nd == 1:
+        return flatnotmasked_contiguous(a)
+    #
+    result = []
+    #
+    other = (axis+1)%2
+    idx = [0,0]
+    idx[axis] = slice(None,None)
+    #
+    for i in range(a.shape[other]):
+        idx[other] = i
+        result.append( flatnotmasked_contiguous(a[idx]) )
+    return result
+
+################################################################################
+if __name__ == '__main__':
+    #
+    import numpy as N
+    from maskedarray.testutils import assert_equal
+    if 1:
+        b = ones(5)
+        m = [1,0,0,0,0]
+        d = masked_array(b,mask=m)
+        c = mr_[d,0,0,d]

Added: trunk/scipy/sandbox/maskedarray/morestats.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/morestats.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/morestats.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,406 @@
+"""
+Generic statistics functions, with support to MA.
+
+:author: Pierre GF Gerard-Marchant
+:contact: pierregm_at_uga_edu
+:date: $Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $
+:version: $Id: morestats.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+
+import numpy
+from numpy import bool_, float_, int_, ndarray, \
+    sqrt,\
+    arange, empty,\
+    r_
+from numpy import array as narray
+import numpy.core.numeric as numeric
+from numpy.core.numeric import concatenate
+
+import scipy.sandbox.maskedarray as MA
+from scipy.sandbox.maskedarray.core import masked, nomask, MaskedArray, masked_array
+from scipy.sandbox.maskedarray.extras import apply_along_axis, dot
+from scipy.sandbox.maskedarray.mstats import trim_both, trimmed_stde, mquantiles, mmedian, stde_median
+
+from scipy.stats.distributions import norm, beta, t, binom
+from scipy.stats.morestats import find_repeats
+
+__all__ = ['hdquantiles', 'hdmedian', 'hdquantiles_sd',
+           'trimmed_mean_ci', 'mjci', 'rank_data']
+
+
+#####--------------------------------------------------------------------------
+#---- --- Quantiles ---
+#####--------------------------------------------------------------------------
+def hdquantiles(data, prob=list([.25,.5,.75]), axis=None, var=False,):
+    """Computes quantile estimates with the Harrell-Davis method, where the estimates
+are calculated as a weighted linear combination of order statistics.
+
+*Parameters* :
+    data: {ndarray}
+        Data array.
+    prob: {sequence}
+        Sequence of quantiles to compute.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+    var : {boolean}
+        Whether to return the variance of the estimate.
+
+*Returns*
+    A (p,) array of quantiles (if ``var`` is False), or a (2,p) array of quantiles
+    and variances (if ``var`` is True), where ``p`` is the number of quantiles.
+
+:Note:
+    The function is restricted to 2D arrays.
+    """
+    def _hd_1D(data,prob,var):
+        "Computes the HD quantiles for a 1D array. Returns nan for invalid data."
+        xsorted = numpy.squeeze(numpy.sort(data.compressed().view(ndarray)))
+        # Don't use length here, in case we have a numpy scalar
+        n = xsorted.size
+        #.........
+        hd = empty((2,len(prob)), float_)
+        if n < 2:
+            hd.flat = numpy.nan
+            if var:
+                return hd
+            return hd[0]
+        #.........
+        v = arange(n+1) / float(n)
+        betacdf = beta.cdf
+        for (i,p) in enumerate(prob):
+            _w = betacdf(v, (n+1)*p, (n+1)*(1-p))
+            w = _w[1:] - _w[:-1]
+            hd_mean = dot(w, xsorted)
+            hd[0,i] = hd_mean
+            #
+            hd[1,i] = dot(w, (xsorted-hd_mean)**2)
+            #
+        hd[0, prob == 0] = xsorted[0]
+        hd[0, prob == 1] = xsorted[-1]
+        if var:
+            hd[1, prob == 0] = hd[1, prob == 1] = numpy.nan
+            return hd
+        return hd[0]
+    # Initialization & checks ---------
+    data = masked_array(data, copy=False, dtype=float_)
+    p = numpy.array(prob, copy=False, ndmin=1)
+    # Computes quantiles along axis (or globally)
+    if (axis is None) or (data.ndim == 1):
+        result = _hd_1D(data, p, var)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        result = apply_along_axis(_hd_1D, axis, data, p, var)
+    #
+    return masked_array(result, mask=numpy.isnan(result))
+
+#..............................................................................
+def hdmedian(data, axis=-1, var=False):
+    """Returns the Harrell-Davis estimate of the median along the given axis.
+
+*Parameters* :
+    data: {ndarray}
+        Data array.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+    var : {boolean}
+        Whether to return the variance of the estimate.
+    """
+    result = hdquantiles(data,[0.5], axis=axis, var=var)
+    return result.squeeze()
+
+
+#..............................................................................
+def hdquantiles_sd(data, prob=list([.25,.5,.75]), axis=None):
+    """Computes the standard error of the Harrell-Davis quantile estimates by jackknife.
+
+
+*Parameters* :
+    data: {ndarray}
+        Data array.
+    prob: {sequence}
+        Sequence of quantiles to compute.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+
+*Note*:
+    The function is restricted to 2D arrays.
+    """
+    def _hdsd_1D(data,prob):
+        "Computes the std error for 1D arrays."
+        xsorted = numpy.sort(data.compressed())
+        n = len(xsorted)
+        #.........
+        hdsd = empty(len(prob), float_)
+        if n < 2:
+            hdsd.flat = numpy.nan
+        #.........
+        vv = arange(n) / float(n-1)
+        betacdf = beta.cdf
+        #
+        for (i,p) in enumerate(prob):
+            _w = betacdf(vv, (n+1)*p, (n+1)*(1-p))
+            w = _w[1:] - _w[:-1]
+            mx_ = numpy.fromiter([dot(w,xsorted[r_[range(0,k),
+                                                   range(k+1,n)].astype(int_)])
+                                  for k in range(n)], dtype=float_)
+            mx_var = numpy.array(mx_.var(), copy=False, ndmin=1) * n / float(n-1)
+            hdsd[i] = float(n-1) * sqrt(numpy.diag(mx_var).diagonal() / float(n))
+        return hdsd
+    # Initialization & checks ---------
+    data = masked_array(data, copy=False, dtype=float_)
+    p = numpy.array(prob, copy=False, ndmin=1)
+    # Computes quantiles along axis (or globally)
+    if (axis is None):
+        result = _hdsd_1D(data.compressed(), p)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        result = apply_along_axis(_hdsd_1D, axis, data, p)
+    #
+    return masked_array(result, mask=numpy.isnan(result)).ravel()
+
+
+#####--------------------------------------------------------------------------
+#---- --- Confidence intervals ---
+#####--------------------------------------------------------------------------
+
+def trimmed_mean_ci(data, proportiontocut=0.2, alpha=0.05, axis=None):
+    """Returns the selected confidence interval of the trimmed mean along the
+given axis.
+
+*Parameters* :
+    data : {sequence}
+        Input data. The data is transformed to a masked array
+    proportiontocut : {float}
+        Proportion of the data to cut from each side of the data .
+        As a result, (2*proportiontocut*n) values are actually trimmed.
+    alpha : {float}
+        Confidence level of the intervals.
+    axis : {integer}
+        Axis along which to cut. If None, uses a flattened version of the input.
+    """
+    data = masked_array(data, copy=False)
+    trimmed = trim_both(data, proportiontocut=proportiontocut, axis=axis)
+    tmean = trimmed.mean(axis)
+    tstde = trimmed_stde(data, proportiontocut=proportiontocut, axis=axis)
+    df = trimmed.count(axis) - 1
+    tppf = t.ppf(1-alpha/2.,df)
+    return numpy.array((tmean - tppf*tstde, tmean+tppf*tstde))
+
+#..............................................................................
+def mjci(data, prob=[0.25,0.5,0.75], axis=None):
+    """Returns the Maritz-Jarrett estimators of the standard error of selected
+experimental quantiles of the data.
+
+*Parameters* :
+    data: {ndarray}
+        Data array.
+    prob: {sequence}
+        Sequence of quantiles to compute.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+    """
+    def _mjci_1D(data, p):
+        data = data.compressed()
+        sorted = numpy.sort(data)
+        n = data.size
+        prob = (numpy.array(p) * n + 0.5).astype(int_)
+        betacdf = beta.cdf
+        #
+        mj = empty(len(prob), float_)
+        x = arange(1,n+1, dtype=float_) / n
+        y = x - 1./n
+        for (i,m) in enumerate(prob):
+            (m1,m2) = (m-1, n-m)
+            W = betacdf(x,m-1,n-m) - betacdf(y,m-1,n-m)
+            C1 = numpy.dot(W,sorted)
+            C2 = numpy.dot(W,sorted**2)
+            mj[i] = sqrt(C2 - C1**2)
+        return mj
+    #
+    data = masked_array(data, copy=False)
+    assert data.ndim <= 2, "Array should be 2D at most !"
+    p = numpy.array(prob, copy=False, ndmin=1)
+    # Computes quantiles along axis (or globally)
+    if (axis is None):
+        return _mjci_1D(data, p)
+    else:
+        return apply_along_axis(_mjci_1D, axis, data, p)
+
+#..............................................................................
+def mquantiles_cimj(data, prob=[0.25,0.50,0.75], alpha=0.05, axis=None):
+    """Computes the alpha confidence interval for the selected quantiles of the
+data, with Maritz-Jarrett estimators.
+
+*Parameters* :
+    data: {ndarray}
+        Data array.
+    prob: {sequence}
+        Sequence of quantiles to compute.
+    alpha : {float}
+        Confidence level of the intervals.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+    """
+    alpha = min(alpha, 1-alpha)
+    z = norm.ppf(1-alpha/2.)
+    xq = mquantiles(data, prob, alphap=0, betap=0, axis=axis)
+    smj = mjci(data, prob, axis=axis)
+    return (xq - z * smj, xq + z * smj)
+
+
+#.............................................................................
+def median_cihs(data, alpha=0.05, axis=None):
+    """Computes the alpha-level confidence interval for the median of the data,
+following the Hettmasperger-Sheather method.
+
+*Parameters* :
+    data : {sequence}
+        Input data. Masked values are discarded. The input should be 1D only, or
+        axis should be set to None.
+    alpha : {float}
+        Confidence level of the intervals.
+    axis : {integer}
+        Axis along which to compute the quantiles. If None, use a flattened array.
+    """
+    def _cihs_1D(data, alpha):
+        data = numpy.sort(data.compressed())
+        n = len(data)
+        alpha = min(alpha, 1-alpha)
+        k = int(binom._ppf(alpha/2., n, 0.5))
+        gk = binom.cdf(n-k,n,0.5) - binom.cdf(k-1,n,0.5)
+        if gk < 1-alpha:
+            k -= 1
+            gk = binom.cdf(n-k,n,0.5) - binom.cdf(k-1,n,0.5)
+        gkk = binom.cdf(n-k-1,n,0.5) - binom.cdf(k,n,0.5)
+        I = (gk - 1 + alpha)/(gk - gkk)
+        lambd = (n-k) * I / float(k + (n-2*k)*I)
+        lims = (lambd*data[k] + (1-lambd)*data[k-1],
+                lambd*data[n-k-1] + (1-lambd)*data[n-k])
+        return lims
+    data = masked_array(data, copy=False)
+    # Computes quantiles along axis (or globally)
+    if (axis is None):
+        result = _cihs_1D(data.compressed(), p, var)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        result = apply_along_axis(_cihs_1D, axis, data, alpha)
+    #
+    return result
+
+#..............................................................................
+def compare_medians_ms(group_1, group_2, axis=None):
+    """Compares the medians from two independent groups along the given axis.
+
+The comparison is performed using the McKean-Schrader estimate of the standard
+error of the medians.
+
+*Parameters* :
+    group_1 : {sequence}
+        First dataset.
+    group_2 : {sequence}
+        Second dataset.
+    axis : {integer}
+        Axis along which the medians are estimated. If None, the arrays are flattened.
+
+*Returns* :
+    A (p,) array of comparison values.
+
+    """
+    (med_1, med_2) = (mmedian(group_1, axis=axis), mmedian(group_2, axis=axis))
+    (std_1, std_2) = (stde_median(group_1, axis=axis),
+                      stde_median(group_2, axis=axis))
+    W = abs(med_1 - med_2) / sqrt(std_1**2 + std_2**2)
+    return 1 - norm.cdf(W)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Ranking ---
+#####--------------------------------------------------------------------------
+
+#..............................................................................
+def rank_data(data, axis=None, use_missing=False):
+    """Returns the rank (also known as order statistics) of each data point
+along the given axis.
+
+If some values are tied, their rank is averaged.
+If some values are masked, their rank is set to 0 if use_missing is False, or
+set to the average rank of the unmasked values if use_missing is True.
+
+*Parameters* :
+    data : {sequence}
+        Input data. The data is transformed to a masked array
+    axis : {integer}
+        Axis along which to perform the ranking. If None, the array is first
+        flattened. An exception is raised if the axis is specified for arrays
+        with a dimension larger than 2
+    use_missing : {boolean}
+        Whether the masked values have a rank of 0 (False) or equal to the
+        average rank of the unmasked values (True).
+    """
+    #
+    def _rank1d(data, use_missing=False):
+        n = data.count()
+        rk = numpy.empty(data.size, dtype=float_)
+        idx = data.argsort()
+        rk[idx[:n]] = numpy.arange(1,n+1)
+        #
+        if use_missing:
+            rk[idx[n:]] = (n+1)/2.
+        else:
+            rk[idx[n:]] = 0
+        #
+        repeats = find_repeats(data)
+        for r in repeats[0]:
+            condition = (data==r).filled(False)
+            rk[condition] = rk[condition].mean()
+        return rk
+    #
+    data = masked_array(data, copy=False)
+    if axis is None:
+        if data.ndim > 1:
+            return _rank1d(data.ravel(), use_missing).reshape(data.shape)
+        else:
+            return _rank1d(data, use_missing)
+    else:
+        return apply_along_axis(_rank1d, axis, data, use_missing)
+
+###############################################################################
+if __name__ == '__main__':
+
+    if 0:
+        from maskedarray.testutils import assert_almost_equal
+        data = [0.706560797,0.727229578,0.990399276,0.927065621,0.158953014,
+            0.887764025,0.239407086,0.349638551,0.972791145,0.149789972,
+            0.936947700,0.132359948,0.046041972,0.641675031,0.945530547,
+            0.224218684,0.771450991,0.820257774,0.336458052,0.589113496,
+            0.509736129,0.696838829,0.491323573,0.622767425,0.775189248,
+            0.641461450,0.118455200,0.773029450,0.319280007,0.752229111,
+            0.047841438,0.466295911,0.583850781,0.840581845,0.550086491,
+            0.466470062,0.504765074,0.226855960,0.362641207,0.891620942,
+            0.127898691,0.490094097,0.044882048,0.041441695,0.317976349,
+            0.504135618,0.567353033,0.434617473,0.636243375,0.231803616,
+            0.230154113,0.160011327,0.819464108,0.854706985,0.438809221,
+            0.487427267,0.786907310,0.408367937,0.405534192,0.250444460,
+            0.995309248,0.144389588,0.739947527,0.953543606,0.680051621,
+            0.388382017,0.863530727,0.006514031,0.118007779,0.924024803,
+            0.384236354,0.893687694,0.626534881,0.473051932,0.750134705,
+            0.241843555,0.432947602,0.689538104,0.136934797,0.150206859,
+            0.474335206,0.907775349,0.525869295,0.189184225,0.854284286,
+            0.831089744,0.251637345,0.587038213,0.254475554,0.237781276,
+            0.827928620,0.480283781,0.594514455,0.213641488,0.024194386,
+            0.536668589,0.699497811,0.892804071,0.093835427,0.731107772]
+        #
+        assert_almost_equal(hdquantiles(data,[0., 1.]),
+                            [0.006514031, 0.995309248])
+        hdq = hdquantiles(data,[0.25, 0.5, 0.75])
+        assert_almost_equal(hdq, [0.253210762, 0.512847491, 0.762232442,])
+        hdq = hdquantiles_sd(data,[0.25, 0.5, 0.75])
+        assert_almost_equal(hdq, [0.03786954, 0.03805389, 0.03800152,], 4)
+        #
+        data = numpy.array(data).reshape(10,10)
+        hdq = hdquantiles(data,[0.25,0.5,0.75],axis=0)

Added: trunk/scipy/sandbox/maskedarray/mpl_maskedarray.patch
===================================================================
--- trunk/scipy/sandbox/maskedarray/mpl_maskedarray.patch	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/mpl_maskedarray.patch	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,12 @@
+diff -urNp matplotlib/numerix/ma.init/__init__.py matplotlib/numerix/ma/__init__.py
+--- matplotlib/numerix/ma.init/__init__.py	2006-08-19 16:21:56.000000000 -0400
++++ matplotlib/numerix/ma/__init__.py	2006-11-29 12:48:14.000000000 -0500
+@@ -9,7 +9,9 @@ elif which[0] == "numeric":
+     nomask = None
+     getmaskorNone = getmask
+ elif which[0] == "numpy":
+-    from numpy.core.ma import *
++    from maskedarray import *
+     def getmaskorNone(obj):
+         _msk = getmask(obj)
+         if _msk is nomask:

Added: trunk/scipy/sandbox/maskedarray/mrecords.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/mrecords.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/mrecords.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,717 @@
+"""mrecords
+Defines a class of record arrays supporting masked arrays.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: mrecords.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import sys
+import types
+
+import numpy
+from numpy import bool_, complex_, float_, int_, str_, object_
+from numpy import array as narray
+import numpy.core.numeric as numeric
+import numpy.core.numerictypes as ntypes
+from numpy.core.defchararray import chararray
+from numpy.core.records import find_duplicate
+
+from numpy.core.records import format_parser, record, recarray
+from numpy.core.records import fromarrays as recfromarrays
+
+ndarray = numeric.ndarray
+_byteorderconv = numpy.core.records._byteorderconv
+_typestr = ntypes._typestr
+
+import scipy.sandbox.maskedarray
+from scipy.sandbox.maskedarray import MaskedArray, masked, nomask, masked_array,\
+    make_mask, mask_or, getmask, getmaskarray, filled
+from scipy.sandbox.maskedarray.core import default_fill_value, masked_print_option
+
+import warnings
+
+reserved_fields = ['_data','_mask','_fieldmask', 'dtype']
+
+def _getformats(data):
+    "Returns the formats of each array of arraylist as a comma-separated string."
+    if hasattr(data,'dtype'):
+        return ",".join([desc[1] for desc in data.dtype.descr])
+
+    formats = ''
+    for obj in data:
+        obj = numeric.asarray(obj)
+#        if not isinstance(obj, ndarray):
+##        if not isinstance(obj, ndarray):
+#            raise ValueError, "item in the array list must be an ndarray."
+        formats += _typestr[obj.dtype.type]
+        if issubclass(obj.dtype.type, ntypes.flexible):
+            formats += `obj.itemsize`
+        formats += ','
+    return formats[:-1]
+
+def _checknames(descr, names=None):
+    """Checks that the field names of the descriptor ``descr`` are not some
+reserved keywords. If this is the case, a default 'f%i' is substituted.
+If the argument `names` is not None, updates the field names to valid names.
+    """
+    ndescr = len(descr)
+    default_names = ['f%i' % i for i in range(ndescr)]
+    if names is None:
+        new_names = default_names
+    else:
+        if isinstance(names, (tuple, list)):
+            new_names = names
+        elif isinstance(names, str):
+            new_names = names.split(',')
+        else:
+            raise NameError, "illegal input names %s" % `names`
+        nnames = len(new_names)
+        if nnames < ndescr:
+            new_names += default_names[nnames:]
+    ndescr = []
+    for (n, d, t) in zip(new_names, default_names, descr.descr):
+        if n in reserved_fields:
+            if t[0] in reserved_fields:
+                ndescr.append((d,t[1]))
+            else:
+                ndescr.append(t)
+        else:
+            ndescr.append((n,t[1]))
+    return numeric.dtype(ndescr)
+
+
+
+class MaskedRecords(MaskedArray, object):
+    """
+
+*IVariables*:
+    _data : {recarray}
+        Underlying data, as a record array.
+    _mask : {boolean array}
+        Mask of the records. A record is masked when all its fields are masked.
+    _fieldmask : {boolean recarray}
+        Record array of booleans, setting the mask of each individual field of each record.
+    _fill_value : {record}
+        Filling values for each field.
+    """
+    _defaultfieldmask = nomask
+    _defaulthardmask = False
+    def __new__(cls, data, mask=nomask, dtype=None,
+                hard_mask=False, fill_value=None,
+#                offset=0, strides=None,
+                formats=None, names=None, titles=None,
+                byteorder=None, aligned=False):
+        # Get the new descriptor ................
+        if dtype is not None:
+            descr = numeric.dtype(dtype)
+        else:
+            if formats is None:
+                formats = _getformats(data)
+            parsed = format_parser(formats, names, titles, aligned, byteorder)
+            descr = parsed._descr
+        if names is not None:
+            descr = _checknames(descr,names)
+        _names = descr.names
+        mdescr = [(n,'|b1') for n in _names]
+        # get the shape .........................
+        try:
+            shape = numeric.asarray(data[0]).shape
+        except IndexError:
+            shape = len(data.dtype)
+        if isinstance(shape, int):
+            shape = (shape,)
+        # Construct the _data recarray ..........
+        if isinstance(data, record):
+            _data = numeric.asarray(data).view(recarray)
+            _fieldmask = mask
+        elif isinstance(data, MaskedRecords):
+            _data = data._data
+            _fieldmask = data._fieldmask
+        elif isinstance(data, recarray):
+            _data = data
+            if mask is nomask:
+                _fieldmask = data.astype(mdescr)
+                _fieldmask.flat = tuple([False]*len(mdescr))
+            else:
+                _fieldmask = mask
+        elif (isinstance(data, (tuple, numpy.void)) or\
+              hasattr(data,'__len__') and isinstance(data[0], (tuple, numpy.void))):
+            data = numeric.array(data, dtype=descr).view(recarray)
+            _data = data
+            if mask is nomask:
+                _fieldmask = data.astype(mdescr)
+                _fieldmask.flat = tuple([False]*len(mdescr))
+            else:
+                _fieldmask = mask
+        else:
+            _data = recarray(shape, dtype=descr)
+            _fieldmask = recarray(shape, dtype=mdescr)
+            for (n,v) in zip(_names, data):
+                _data[n] = numeric.asarray(v).view(ndarray)
+                _fieldmask[n] = getmaskarray(v)
+        #........................................
+        _data = _data.view(cls)
+        _data._fieldmask = _fieldmask
+        _data._hardmask = hard_mask
+        if fill_value is None:
+            _data._fill_value = [default_fill_value(numeric.dtype(d[1]))
+                                 for d in descr.descr]
+        else:
+            _data._fill_value = fill_value
+        return _data
+
+    def __array_finalize__(self,obj):
+        if isinstance(obj, MaskedRecords):
+            self.__dict__.update(_fieldmask=obj._fieldmask,
+                                 _hardmask=obj._hardmask,
+                                 _fill_value=obj._fill_value
+                                 )
+        else:
+            self.__dict__.update(_fieldmask = nomask,
+                                 _hardmask = False,
+                                 fill_value = None
+                                )
+        return
+
+    def _getdata(self):
+        "Returns the data as a recarray."
+        return self.view(recarray)
+    _data = property(fget=_getdata)
+
+    #......................................................
+    def __getattribute__(self, attr):
+        "Returns the given attribute."
+        try:
+            # Returns a generic attribute
+            return object.__getattribute__(self,attr)
+        except AttributeError:
+            # OK, so attr must be a field name
+            pass
+        # Get the list of fields ......
+        _names = self.dtype.names
+        if attr in _names:
+            _data = self._data
+            _mask = self._fieldmask
+#            obj = masked_array(_data.__getattribute__(attr), copy=False,
+#                               mask=_mask.__getattribute__(attr))
+            # Use a view in order to avoid the copy of the mask in MaskedArray.__new__
+            obj = narray(_data.__getattribute__(attr), copy=False).view(MaskedArray)
+            obj._mask = _mask.__getattribute__(attr)
+            if not obj.ndim and obj._mask:
+                return masked
+            return obj
+        raise AttributeError,"No attribute '%s' !" % attr
+
+    def __setattr__(self, attr, val):
+        "Sets the attribute attr to the value val."
+        newattr = attr not in self.__dict__
+        try:
+            # Is attr a generic attribute ?
+            ret = object.__setattr__(self, attr, val)
+        except:
+            # Not a generic attribute: exit if it's not a valid field
+            fielddict = self.dtype.names or {}
+            if attr not in fielddict:
+                exctype, value = sys.exc_info()[:2]
+                raise exctype, value
+        else:
+            if attr not in list(self.dtype.names) + ['_mask','mask']:
+                return ret
+            if newattr:         # We just added this one
+                try:            #  or this setattr worked on an internal
+                                #  attribute.
+                    object.__delattr__(self, attr)
+                except:
+                    return ret
+        # Case #1.: Basic field ............
+        base_fmask = self._fieldmask
+        _names = self.dtype.names
+        if attr in _names:
+            fval = filled(val)
+            mval = getmaskarray(val)
+            if self._hardmask:
+                mval = mask_or(mval, base_fmask.__getattr__(attr))
+            self._data.__setattr__(attr, fval)
+            base_fmask.__setattr__(attr, mval)
+            return
+        elif attr == '_mask':
+            self.__setmask__(val)
+            return
+    #............................................
+    def __getitem__(self, indx):
+        """Returns all the fields sharing the same fieldname base.
+The fieldname base is either `_data` or `_mask`."""
+        _localdict = self.__dict__
+        _data = self._data
+        # We want a field ........
+        if isinstance(indx, str):
+            obj = _data[indx].view(MaskedArray)
+            obj._set_mask(_localdict['_fieldmask'][indx])
+            # Force to nomask if the mask is empty
+            if not obj._mask.any():
+                obj._mask = nomask
+            return obj
+        # We want some elements ..
+        # First, the data ........
+        obj = ndarray.__getitem__(self, indx)
+        if isinstance(obj, numpy.void):
+            obj = self.__class__(obj, dtype=self.dtype)
+        else:
+            obj = obj.view(type(self))
+        obj._fieldmask = numpy.asarray(_localdict['_fieldmask'][indx]).view(recarray)
+        return obj
+    #............................................
+    def __setitem__(self, indx, value):
+        "Sets the given record to value."
+        MaskedArray.__setitem__(self, indx, value)
+
+
+    def __setslice__(self, i, j, value):
+        "Sets the slice described by [i,j] to `value`."
+        _localdict = self.__dict__
+        d = self._data
+        m = _localdict['_fieldmask']
+        names = self.dtype.names
+        if value is masked:
+            for n in names:
+                m[i:j][n] = True
+        elif not self._hardmask:
+            fval = filled(value)
+            mval = getmaskarray(value)
+            for n in names:
+                d[n][i:j] = fval
+                m[n][i:j] = mval
+        else:
+            mindx = getmaskarray(self)[i:j]
+            dval = numeric.asarray(value)
+            valmask = getmask(value)
+            if valmask is nomask:
+                for n in names:
+                    mval = mask_or(m[n][i:j], valmask)
+                    d[n][i:j][~mval] = value
+            elif valmask.size > 1:
+                for n in names:
+                    mval = mask_or(m[n][i:j], valmask)
+                    d[n][i:j][~mval] = dval[~mval]
+                    m[n][i:j] = mask_or(m[n][i:j], mval)
+        self._fieldmask = m
+
+    #.....................................................
+    def __setmask__(self, mask):
+        "Sets the mask."
+        names = self.dtype.names
+        fmask = self.__dict__['_fieldmask']
+        newmask = make_mask(mask, copy=False)
+#        self.unshare_mask()
+        if self._hardmask:
+            for n in names:
+                fmask[n].__ior__(newmask)
+        else:
+            for n in names:
+                fmask[n].flat = newmask
+        return
+
+    def _getmask(self):
+        """Returns the mask of the mrecord: a record is masked when all the fields
+are masked."""
+        if self.size > 1:
+            return self._fieldmask.view((bool_, len(self.dtype))).all(1)
+
+    _setmask = __setmask__
+    _mask = property(fget=_getmask, fset=_setmask)
+
+    #......................................................
+    def __str__(self):
+        "Calculates the string representation."
+        if self.size > 1:
+            mstr = ["(%s)" % ",".join([str(i) for i in s])
+                    for s in zip(*[getattr(self,f) for f in self.dtype.names])]
+            return "[%s]" % ", ".join(mstr)
+        else:
+            mstr = ["%s" % ",".join([str(i) for i in s])
+                    for s in zip([getattr(self,f) for f in self.dtype.names])]
+            return "(%s)" % ", ".join(mstr)
+
+    def __repr__(self):
+        "Calculates the repr representation."
+        _names = self.dtype.names
+        fmt = "%%%is : %%s" % (max([len(n) for n in _names])+4,)
+        reprstr = [fmt % (f,getattr(self,f)) for f in self.dtype.names]
+        reprstr.insert(0,'masked_records(')
+        reprstr.extend([fmt % ('    fill_value', self._fill_value),
+                         '              )'])
+        return str("\n".join(reprstr))
+    #......................................................
+    def view(self, obj):
+        """Returns a view of the mrecarray."""
+        try:
+            if issubclass(obj, ndarray):
+                return ndarray.view(self, obj)
+        except TypeError:
+            pass
+        dtype = numeric.dtype(obj)
+        if dtype.fields is None:
+            return self.__array__().view(dtype)
+        return ndarray.view(self, obj)
+    #......................................................
+    def filled(self, fill_value=None):
+        """Returns an array of the same class as ``_data``, with masked values
+filled with ``fill_value``. If ``fill_value`` is None, ``self.fill_value`` is
+used instead.
+
+Subclassing is preserved.
+
+        """
+        _localdict = self.__dict__
+        d = self._data
+        fm = _localdict['_fieldmask']
+        if not numeric.asarray(fm, dtype=bool_).any():
+            return d
+        #
+        if fill_value is None:
+            value = _localdict['_fill_value']
+        else:
+            value = fill_value
+            if numeric.size(value) == 1:
+                value = [value,] * len(self.dtype)
+        #
+        if self is masked:
+            result = numeric.asanyarray(value)
+        else:
+            result = d.copy()
+            for (n, v) in zip(d.dtype.names, value):
+                numpy.putmask(numeric.asarray(result[n]),
+                              numeric.asarray(fm[n]), v)
+        return result
+    #............................................
+    def harden_mask(self):
+        "Forces the mask to hard"
+        self._hardmask = True
+    def soften_mask(self):
+        "Forces the mask to soft"
+        self._hardmask = False
+    #.............................................
+    def copy(self):
+        """Returns a copy of the masked record."""
+        _localdict = self.__dict__
+        return MaskedRecords(self._data.copy(),
+                        mask=_localdict['_fieldmask'].copy(),
+                       dtype=self.dtype)
+    #.............................................
+
+
+#####---------------------------------------------------------------------------
+#---- --- Constructors ---
+#####---------------------------------------------------------------------------
+
+def fromarrays(arraylist, dtype=None, shape=None, formats=None,
+               names=None, titles=None, aligned=False, byteorder=None):
+    """Creates a mrecarray from a (flat) list of masked arrays.
+
+*Parameters*:
+    arraylist : {sequence}
+        A list of (masked) arrays. Each element of the sequence is first converted
+        to a masked array if needed. If a 2D array is passed as argument, it is
+        processed line by line
+    dtype : {numeric.dtype}
+        Data type descriptor.
+    {shape} : {integer}
+        Number of records. If None, ``shape`` is defined from the shape of the
+        first array in the list.
+    formats : {sequence}
+        Sequence of formats for each individual field. If None, the formats will
+        be autodetected by inspecting the fields and selecting the highest dtype
+        possible.
+    names : {sequence}
+        Sequence of the names of each field.
+    -titles : {sequence}
+      (Description to write)
+    aligned : {boolean}
+      (Description to write, not used anyway)
+    byteorder: {boolean}
+      (Description to write, not used anyway)
+
+*Notes*:
+    Lists of tuples should be preferred over lists of lists for faster processing.
+    """
+    arraylist = [masked_array(x) for x in arraylist]
+    # Define/check the shape.....................
+    if shape is None or shape == 0:
+        shape = arraylist[0].shape
+    if isinstance(shape, int):
+        shape = (shape,)
+    # Define formats from scratch ...............
+    if formats is None and dtype is None:
+        formats = _getformats(arraylist)
+    # Define the dtype ..........................
+    if dtype is not None:
+        descr = numeric.dtype(dtype)
+        _names = descr.names
+    else:
+        parsed = format_parser(formats, names, titles, aligned, byteorder)
+        _names = parsed._names
+        descr = parsed._descr
+    # Determine shape from data-type.............
+    if len(descr) != len(arraylist):
+        msg = "Mismatch between the number of fields (%i) and the number of "\
+              "arrays (%i)"
+        raise ValueError, msg % (len(descr), len(arraylist))
+    d0 = descr[0].shape
+    nn = len(d0)
+    if nn > 0:
+        shape = shape[:-nn]
+    # Make sure the shape is the correct one ....
+    for k, obj in enumerate(arraylist):
+        nn = len(descr[k].shape)
+        testshape = obj.shape[:len(obj.shape)-nn]
+        if testshape != shape:
+            raise ValueError, "Array-shape mismatch in array %d" % k
+    # Reconstruct the descriptor, by creating a _data and _mask version
+    return MaskedRecords(arraylist, dtype=descr)
+#..............................................................................
+def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None,
+                titles=None, aligned=False, byteorder=None):
+    """Creates a MaskedRecords from a list of records.
+
+*Parameters*:
+    arraylist : {sequence}
+        A list of (masked) arrays. Each element of the sequence is first converted
+        to a masked array if needed. If a 2D array is passed as argument, it is
+        processed line by line
+    dtype : {numeric.dtype}
+        Data type descriptor.
+    {shape} : {integer}
+        Number of records. If None, ``shape`` is defined from the shape of the
+        first array in the list.
+    formats : {sequence}
+        Sequence of formats for each individual field. If None, the formats will
+        be autodetected by inspecting the fields and selecting the highest dtype
+        possible.
+    names : {sequence}
+        Sequence of the names of each field.
+    -titles : {sequence}
+      (Description to write)
+    aligned : {boolean}
+      (Description to write, not used anyway)
+    byteorder: {boolean}
+      (Description to write, not used anyway)
+
+*Notes*:
+    Lists of tuples should be preferred over lists of lists for faster processing.
+    """
+    # reclist is in fact a mrecarray .................
+    if isinstance(reclist, MaskedRecords):
+        mdescr = reclist.dtype
+        shape = reclist.shape
+        return MaskedRecords(reclist, dtype=mdescr)
+    # No format, no dtype: create from to arrays .....
+    nfields = len(reclist[0])
+    if formats is None and dtype is None:  # slower
+        if isinstance(reclist, recarray):
+            arrlist = [reclist.field(i) for i in range(len(reclist.dtype))]
+            if names is None:
+                names = reclist.dtype.names
+        else:
+            obj = numeric.array(reclist,dtype=object)
+            arrlist = [numeric.array(obj[...,i].tolist())
+                               for i in xrange(nfields)]
+        return MaskedRecords(arrlist, formats=formats, names=names,
+                             titles=titles, aligned=aligned, byteorder=byteorder)
+    # Construct the descriptor .......................
+    if dtype is not None:
+        descr = numeric.dtype(dtype)
+        _names = descr.names
+    else:
+        parsed = format_parser(formats, names, titles, aligned, byteorder)
+        _names = parsed._names
+        descr = parsed._descr
+
+    try:
+        retval = numeric.array(reclist, dtype = descr).view(recarray)
+    except TypeError:  # list of lists instead of list of tuples
+        if (shape is None or shape == 0):
+            shape = len(reclist)*2
+        if isinstance(shape, (int, long)):
+            shape = (shape*2,)
+        if len(shape) > 1:
+            raise ValueError, "Can only deal with 1-d array."
+        retval = recarray(shape, mdescr)
+        for k in xrange(retval.size):
+            retval[k] = tuple(reclist[k])
+        return MaskedRecords(retval, dtype=descr)
+    else:
+        if shape is not None and retval.shape != shape:
+            retval.shape = shape
+    #
+    return MaskedRecords(retval, dtype=descr)
+
+def _guessvartypes(arr):
+    """Tries to guess the dtypes of the str_ ndarray `arr`, by testing element-wise
+conversion. Returns a list of dtypes.
+The array is first converted to ndarray. If the array is 2D, the test is performed
+on the first line. An exception is raised if the file is 3D or more.
+    """
+    vartypes = []
+    arr = numeric.asarray(arr)
+    if len(arr.shape) == 2 :
+        arr = arr[0]
+    elif len(arr.shape) > 2:
+        raise ValueError, "The array should be 2D at most!"
+    # Start the conversion loop .......
+    for f in arr:
+        try:
+            val = int(f)
+        except ValueError:
+            try:
+                val = float(f)
+            except ValueError:
+                try:
+                    val = complex(f)
+                except ValueError:
+                    vartypes.append(arr.dtype)
+                else:
+                    vartypes.append(complex_)
+            else:
+                vartypes.append(float_)
+        else:
+            vartypes.append(int_)
+    return vartypes
+
+def openfile(fname):
+    "Opens the file handle of file `fname`"
+    # A file handle ...................
+    if hasattr(fname, 'readline'):
+        return fname
+    # Try to open the file and guess its type
+    try:
+        f = open(fname)
+    except IOError:
+        raise IOError, "No such file: '%s'" % fname
+    if f.readline()[:2] != "\\x":
+        f.seek(0,0)
+        return f
+    raise NotImplementedError, "Wow, binary file"
+
+
+def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='',
+                 varnames=None, vartypes=None):
+    """Creates a mrecarray from data stored in the file `filename`.
+
+*Parameters* :
+    filename : {file name/handle}
+        Handle of an opened file.
+    delimitor : {string}
+        Alphanumeric character used to separate columns in the file.
+        If None, any (group of) white spacestring(s) will be used.
+    commentchar : {string}
+        Alphanumeric character used to mark the start of a comment.
+    missingchar` : {string}
+        String indicating missing data, and used to create the masks.
+    varnames : {sequence}
+        Sequence of the variable names. If None, a list will be created from
+        the first non empty line of the file.
+    vartypes : {sequence}
+        Sequence of the variables dtypes. If None, it will be estimated from
+        the first non-commented line.
+
+
+    Ultra simple: the varnames are in the header, one line"""
+    # Try to open the file ......................
+    f = openfile(fname)
+    # Get the first non-empty line as the varnames
+    while True:
+        line = f.readline()
+        firstline = line[:line.find(commentchar)].strip()
+        _varnames = firstline.split(delimitor)
+        if len(_varnames) > 1:
+            break
+    if varnames is None:
+        varnames = _varnames
+    # Get the data ..............................
+    _variables = masked_array([line.strip().split(delimitor) for line in f
+                                  if line[0] != commentchar and len(line) > 1])
+    (_, nfields) = _variables.shape
+    # Try to guess the dtype ....................
+    if vartypes is None:
+        vartypes = _guessvartypes(_variables[0])
+    else:
+        vartypes = [numeric.dtype(v) for v in vartypes]
+        if len(vartypes) != nfields:
+            msg = "Attempting to %i dtypes for %i fields!"
+            msg += " Reverting to default."
+            warnings.warn(msg % (len(vartypes), nfields))
+            vartypes = _guessvartypes(_variables[0])
+    # Construct the descriptor ..................
+    mdescr = [(n,f) for (n,f) in zip(varnames, vartypes)]
+    # Get the data and the mask .................
+    # We just need a list of masked_arrays. It's easier to create it like that:
+    _mask = (_variables.T == missingchar)
+    _datalist = [masked_array(a,mask=m,dtype=t)
+                     for (a,m,t) in zip(_variables.T, _mask, vartypes)]
+    return MaskedRecords(_datalist, dtype=mdescr)
+
+#....................................................................
+def addfield(mrecord, newfield, newfieldname=None):
+    """Adds a new field to the masked record array, using `newfield` as data
+and `newfieldname` as name. If `newfieldname` is None, the new field name is
+set to 'fi', where `i` is the number of existing fields.
+    """
+    _data = mrecord._data
+    _mask = mrecord._fieldmask
+    if newfieldname is None or newfieldname in reserved_fields:
+        newfieldname = 'f%i' % len(_data.dtype)
+    newfield = masked_array(newfield)
+    # Get the new data ............
+    # Create a new empty recarray
+    newdtype = numeric.dtype(_data.dtype.descr + \
+                             [(newfieldname, newfield.dtype)])
+    newdata = recarray(_data.shape, newdtype)
+    # Add the exisintg field
+    [newdata.setfield(_data.getfield(*f),*f)
+         for f in _data.dtype.fields.values()]
+    # Add the new field
+    newdata.setfield(newfield._data, *newdata.dtype.fields[newfieldname])
+    newdata = newdata.view(MaskedRecords)
+    # Get the new mask .............
+    # Create a new empty recarray
+    newmdtype = numeric.dtype([(n,bool_) for n in newdtype.names])
+    newmask = recarray(_data.shape, newmdtype)
+    # Add the old masks
+    [newmask.setfield(_mask.getfield(*f),*f)
+         for f in _mask.dtype.fields.values()]
+    # Add the mask of the new field
+    newmask.setfield(getmaskarray(newfield),
+                     *newmask.dtype.fields[newfieldname])
+    newdata._fieldmask = newmask
+    return newdata
+
+################################################################################
+if __name__ == '__main__':
+    import numpy as N
+    from maskedarray.testutils import assert_equal
+    if 1:
+        d = N.arange(5)
+        m = maskedarray.make_mask([1,0,0,1,1])
+        base_d = N.r_[d,d[::-1]].reshape(2,-1).T
+        base_m = N.r_[[m, m[::-1]]].T
+        base = masked_array(base_d, mask=base_m).T
+        mrecord = fromarrays(base,dtype=[('a',N.float_),('b',N.float_)])
+        mrec = MaskedRecords(mrecord.copy())
+        #
+    if 1:
+        mrec = mrec.copy()
+        mrec.harden_mask()
+        assert(mrec._hardmask)
+        mrec._mask = nomask
+        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+        mrec.soften_mask()
+        assert(not mrec._hardmask)
+        mrec.mask = nomask
+        tmp = mrec['b']._mask
+        assert(mrec['b']._mask is nomask)
+        assert_equal(mrec['a']._mask,mrec['b']._mask)

Added: trunk/scipy/sandbox/maskedarray/mstats.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/mstats.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/mstats.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,433 @@
+"""
+Generic statistics functions, with support to MA.
+
+:author: Pierre GF Gerard-Marchant
+:contact: pierregm_at_uga_edu
+:date: $Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $
+:version: $Id: mstats.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+
+import numpy
+from numpy import bool_, float_, int_, \
+    sqrt
+from numpy import array as narray
+import numpy.core.numeric as numeric
+from numpy.core.numeric import concatenate
+
+import scipy.sandbox.maskedarray
+from scipy.sandbox.maskedarray.core import masked, nomask, MaskedArray, masked_array
+from scipy.sandbox.maskedarray.extras import apply_along_axis, dot
+
+__all__ = ['cov','meppf','plotting_positions','meppf','mmedian','mquantiles',
+           'stde_median','trim_tail','trim_both','trimmed_mean','trimmed_stde',
+           'winsorize']
+
+#####--------------------------------------------------------------------------
+#---- -- Trimming ---
+#####--------------------------------------------------------------------------
+
+def winsorize(data, alpha=0.2):
+    """Returns a Winsorized version of the input array.
+
+The (alpha/2.) lowest values are set to the (alpha/2.)th percentile, and
+the (alpha/2.) highest values are set to the (1-alpha/2.)th percentile
+Masked values are skipped.
+
+*Parameters*:
+    data : {ndarray}
+        Input data to Winsorize. The data is first flattened.
+    alpha : {float}, optional
+        Percentage of total Winsorization : alpha/2. on the left, alpha/2. on the right
+    """
+    data = masked_array(data, copy=False).ravel()
+    idxsort = data.argsort()
+    (nsize, ncounts) = (data.size, data.count())
+    ntrim = int(alpha * ncounts)
+    (xmin,xmax) = data[idxsort[[ntrim, ncounts-nsize-ntrim-1]]]
+    return masked_array(numpy.clip(data, xmin, xmax), mask=data._mask)
+
+#..............................................................................
+def trim_both(data, proportiontocut=0.2, axis=None):
+    """Trims the data by masking the int(trim*n) smallest and int(trim*n) largest
+values of data along the given axis, where n is the number of unmasked values.
+
+*Parameters*:
+    data : {ndarray}
+        Data to trim.
+    proportiontocut : {float}
+        Percentage of trimming. If n is the number of unmasked values before trimming,
+        the number of values after trimming is (1-2*trim)*n.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    #...................
+    def _trim_1D(data, trim):
+        "Private function: return a trimmed 1D array."
+        nsize = data.size
+        ncounts = data.count()
+        ntrim = int(trim * ncounts)
+        idxsort = data.argsort()
+        data[idxsort[:ntrim]] = masked
+        data[idxsort[ncounts-nsize-ntrim:]] = masked
+        return data
+    #...................
+    data = masked_array(data, copy=False, subok=True)
+    data.unshare_mask()
+    if (axis is None):
+        return _trim_1D(data.ravel(), proportiontocut)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        return apply_along_axis(_trim_1D, axis, data, proportiontocut)
+
+#..............................................................................
+def trim_tail(data, proportiontocut=0.2, tail='left', axis=None):
+    """Trims the data by masking int(trim*n) values from ONE tail of the data
+along the given axis, where n is the number of unmasked values.
+
+*Parameters*:
+    data : {ndarray}
+        Data to trim.
+    proportiontocut : {float}
+        Percentage of trimming. If n is the number of unmasked values before trimming,
+        the number of values after trimming is (1-trim)*n.
+    tail : {string}
+        Trimming direction, in ('left', 'right'). If left, the proportiontocut
+        lowest values are set to the corresponding percentile. If right, the
+        proportiontocut highest values are used instead.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    #...................
+    def _trim_1D(data, trim, left):
+        "Private function: return a trimmed 1D array."
+        nsize = data.size
+        ncounts = data.count()
+        ntrim = int(trim * ncounts)
+        idxsort = data.argsort()
+        if left:
+            data[idxsort[:ntrim]] = masked
+        else:
+            data[idxsort[ncounts-nsize-ntrim:]] = masked
+        return data
+    #...................
+    data = masked_array(data, copy=False, subok=True)
+    data.unshare_mask()
+    #
+    if not isinstance(tail, str):
+        raise TypeError("The tail argument should be in ('left','right')")
+    tail = tail.lower()[0]
+    if tail == 'l':
+        left = True
+    elif tail == 'r':
+        left=False
+    else:
+        raise ValueError("The tail argument should be in ('left','right')")
+    #
+    if (axis is None):
+        return _trim_1D(data.ravel(), proportiontocut, left)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        return apply_along_axis(_trim_1D, axis, data, proportiontocut, left)
+
+#..............................................................................
+def trimmed_mean(data, proportiontocut=0.2, axis=None):
+    """Returns the trimmed mean of the data along the given axis. Trimming is
+performed on both ends of the distribution.
+
+*Parameters*:
+    data : {ndarray}
+        Data to trim.
+    proportiontocut : {float}
+        Proportion of the data to cut from each side of the data .
+        As a result, (2*proportiontocut*n) values are actually trimmed.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    return trim_both(data, proportiontocut=proportiontocut, axis=axis).mean(axis=axis)
+
+#..............................................................................
+def trimmed_stde(data, proportiontocut=0.2, axis=None):
+    """Returns the standard error of the trimmed mean for the input data,
+along the given axis. Trimming is performed on both ends of the distribution.
+
+*Parameters*:
+    data : {ndarray}
+        Data to trim.
+    proportiontocut : {float}
+        Proportion of the data to cut from each side of the data .
+        As a result, (2*proportiontocut*n) values are actually trimmed.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    #........................
+    def _trimmed_stde_1D(data, trim=0.2):
+        "Returns the standard error of the trimmed mean for a 1D input data."
+        winsorized = winsorize(data)
+        nsize = winsorized.count()
+        winstd = winsorized.stdu()
+        return winstd / ((1-2*trim) * numpy.sqrt(nsize))
+    #........................
+    data = masked_array(data, copy=False, subok=True)
+    data.unshare_mask()
+    if (axis is None):
+        return _trimmed_stde_1D(data.ravel(), proportiontocut)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        return apply_along_axis(_trimmed_stde_1D, axis, data, proportiontocut)
+
+#.............................................................................
+def stde_median(data, axis=None):
+    """Returns the McKean-Schrader estimate of the standard error of the sample
+median along the given axis.
+
+
+*Parameters*:
+    data : {ndarray}
+        Data to trim.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    def _stdemed_1D(data):
+        sorted = numpy.sort(data.compressed())
+        n = len(sorted)
+        z = 2.5758293035489004
+        k = int(round((n+1)/2. - z * sqrt(n/4.),0))
+        return ((sorted[n-k] - sorted[k-1])/(2.*z))
+    #
+    data = masked_array(data, copy=False, subok=True)
+    if (axis is None):
+        return _stdemed_1D(data)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        return apply_along_axis(_stdemed_1D, axis, data)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Quantiles ---
+#####--------------------------------------------------------------------------
+
+
+def mquantiles(data, prob=list([.25,.5,.75]), alphap=.4, betap=.4, axis=None):
+    """Computes empirical quantiles for a *1xN* data array.
+Samples quantile are defined by:
+*Q(p) = (1-g).x[i] +g.x[i+1]*
+where *x[j]* is the jth order statistic,
+with *i = (floor(n*p+m))*, *m=alpha+p*(1-alpha-beta)* and *g = n*p + m - i)*.
+
+Typical values of (alpha,beta) are:
+
+    - (0,1)    : *p(k) = k/n* : linear interpolation of cdf (R, type 4)
+    - (.5,.5)  : *p(k) = (k+1/2.)/n* : piecewise linear function (R, type 5)
+    - (0,0)    : *p(k) = k/(n+1)* : (R type 6)
+    - (1,1)    : *p(k) = (k-1)/(n-1)*. In this case, p(k) = mode[F(x[k])].
+      That's R default (R type 7)
+    - (1/3,1/3): *p(k) = (k-1/3)/(n+1/3)*. Then p(k) ~ median[F(x[k])].
+      The resulting quantile estimates are approximately median-unbiased
+      regardless of the distribution of x. (R type 8)
+    - (3/8,3/8): *p(k) = (k-3/8)/(n+1/4)*. Blom.
+      The resulting quantile estimates are approximately unbiased
+      if x is normally distributed (R type 9)
+    - (.4,.4)  : approximately quantile unbiased (Cunnane)
+    - (.35,.35): APL, used with PWM
+
+*Parameters*:
+    x : {sequence}
+        Input data, as a sequence or array of dimension at most 2.
+    prob : {sequence}
+        List of quantiles to compute.
+    alpha : {float}
+        Plotting positions parameter.
+    beta : {float}
+        Plotting positions parameter.
+    axis : {integer}
+        Axis along which to perform the trimming. If None, the input array is first
+        flattened.
+    """
+    def _quantiles1D(data,m,p):
+        x = numpy.sort(data.compressed())
+        n = len(x)
+        if n == 0:
+            return masked_array(numpy.empty(len(p), dtype=float_), mask=True)
+        elif n == 1:
+            return masked_array(numpy.resize(x, p.shape), mask=nomask)
+        aleph = (n*p + m)
+        k = numpy.floor(aleph.clip(1, n-1)).astype(int_)
+        gamma = (aleph-k).clip(0,1)
+        return (1.-gamma)*x[(k-1).tolist()] + gamma*x[k.tolist()]
+
+    # Initialization & checks ---------
+    data = masked_array(data, copy=False)
+    p = narray(prob, copy=False, ndmin=1)
+    m = alphap + p*(1.-alphap-betap)
+    # Computes quantiles along axis (or globally)
+    if (axis is None):
+        return _quantiles1D(data, m, p)
+    else:
+        assert data.ndim <= 2, "Array should be 2D at most !"
+        return apply_along_axis(_quantiles1D, axis, data, m, p)
+
+
+def plotting_positions(data, alpha=0.4, beta=0.4):
+    """Returns the plotting positions (or empirical percentile points) for the
+    data.
+    Plotting positions are defined as (i-alpha)/(n-alpha-beta), where:
+        - i is the rank order statistics
+        - n is the number of unmasked values along the given axis
+        - alpha and beta are two parameters.
+
+    Typical values for alpha and beta are:
+        - (0,1)    : *p(k) = k/n* : linear interpolation of cdf (R, type 4)
+        - (.5,.5)  : *p(k) = (k-1/2.)/n* : piecewise linear function (R, type 5)
+        - (0,0)    : *p(k) = k/(n+1)* : Weibull (R type 6)
+        - (1,1)    : *p(k) = (k-1)/(n-1)*. In this case, p(k) = mode[F(x[k])].
+          That's R default (R type 7)
+        - (1/3,1/3): *p(k) = (k-1/3)/(n+1/3)*. Then p(k) ~ median[F(x[k])].
+          The resulting quantile estimates are approximately median-unbiased
+          regardless of the distribution of x. (R type 8)
+        - (3/8,3/8): *p(k) = (k-3/8)/(n+1/4)*. Blom.
+          The resulting quantile estimates are approximately unbiased
+          if x is normally distributed (R type 9)
+        - (.4,.4)  : approximately quantile unbiased (Cunnane)
+        - (.35,.35): APL, used with PWM
+    """
+    data = masked_array(data, copy=False).reshape(1,-1)
+    n = data.count()
+    plpos = numpy.empty(data.size, dtype=float_)
+    plpos[n:] = 0
+    plpos[data.argsort()[:n]] = (numpy.arange(1,n+1) - alpha)/(n+1-alpha-beta)
+    return masked_array(plpos, mask=data._mask)
+
+meppf = plotting_positions
+
+
+def mmedian(data, axis=None):
+    """Returns the median of data along the given axis. Missing data are discarded."""
+    def _median1D(data):
+        x = numpy.sort(data.compressed())
+        if x.size == 0:
+            return masked
+        return numpy.median(x)
+    data = masked_array(data, subok=True, copy=True)
+    if axis is None:
+        return _median1D(data)
+    else:
+        return apply_along_axis(_median1D, axis, data)
+
+
+def cov(x, y=None, rowvar=True, bias=False, strict=False):
+    """Estimates the covariance matrix.
+
+
+Normalization is by (N-1) where N is the number of observations (unbiased
+estimate).  If bias is True then normalization is by N.
+
+*Parameters*:
+    x : {ndarray}
+        Input data. If x is a 1D array, returns the variance. If x is a 2D array,
+        returns the covariance matrix.
+    y : {ndarray}, optional
+        Optional set of variables.
+    rowvar : {boolean}
+        If rowvar is true, then each row is a variable with obersvations in columns.
+        If rowvar is False, each column is a variable and the observations are in
+        the rows.
+    bias : {boolean}
+        Whether to use a biased or unbiased estimate of the covariance.
+        If bias is True, then the normalization is by N, the number of observations.
+        Otherwise, the normalization is by (N-1)
+    strict : {boolean}
+        If strict is True, masked values are propagated: if a masked value appears in
+        a row or column, the whole row or column is considered masked.
+    """
+    X = narray(x, ndmin=2, subok=True, dtype=float)
+    if X.shape[0] == 1:
+        rowvar = True
+    if rowvar:
+        axis = 0
+        tup = (slice(None),None)
+    else:
+        axis = 1
+        tup = (None, slice(None))
+    #
+    if y is not None:
+        y = narray(y, copy=False, ndmin=2, subok=True, dtype=float)
+        X = concatenate((X,y),axis)
+    #
+    X -= X.mean(axis=1-axis)[tup]
+    n = X.count(1-axis)
+    #
+    if bias:
+        fact = n*1.0
+    else:
+        fact = n-1.0
+    #
+    if not rowvar:
+        return (dot(X.T, X.conj(), strict=False) / fact).squeeze()
+    else:
+        return (dot(X, X.T.conj(), strict=False) / fact).squeeze()
+
+
+def idealfourths(data, axis=None):
+    """Returns an estimate of the interquartile range of the data along the given
+axis, as computed with the ideal fourths.
+    """
+    def _idf(data):
+        x = numpy.sort(data.compressed())
+        n = len(x)
+        (j,h) = divmod(n/4. + 5/12.,1)
+        qlo = (1-h)*x[j] + h*x[j+1]
+        k = n - j
+        qup = (1-h)*x[k] + h*x[k-1]
+        return qup - qlo
+    data = masked_array(data, copy=False)
+    if (axis is None):
+        return _idf(data)
+    else:
+        return apply_along_axis(_idf, axis, data)
+
+
+def rsh(data, points=None):
+    """Evalutates Rosenblatt's shifted histogram estimators for each point
+on the dataset 'data'.
+
+*Parameters* :
+    data : {sequence}
+        Input data. Masked values are ignored.
+    points : {sequence}
+        Sequence of points where to evaluate Rosenblatt shifted histogram.
+        If None, use the data.
+    """
+    data = masked_array(data, copy=False)
+    if points is None:
+        points = data
+    else:
+        points = numpy.array(points, copy=False, ndmin=1)
+    if data.ndim != 1:
+        raise AttributeError("The input array should be 1D only !")
+    n = data.count()
+    h = 1.2 * idealfourths(data) / n**(1./5)
+    nhi = (data[:,None] <= points[None,:] + h).sum(0)
+    nlo = (data[:,None] < points[None,:] - h).sum(0)
+    return (nhi-nlo) / (2.*n*h)
+
+################################################################################
+if __name__ == '__main__':
+    from maskedarray.testutils import assert_almost_equal
+    if 1:
+        a = maskedarray.arange(1,101)
+        a[1::2] = masked
+        b = maskedarray.resize(a, (100,100))
+        assert_almost_equal(mquantiles(b), [25., 50., 75.])
+        assert_almost_equal(mquantiles(b, axis=0), maskedarray.resize(a,(3,100)))
+        assert_almost_equal(mquantiles(b, axis=1),
+                            maskedarray.resize([24.9, 50., 75.1], (100,3)))

Added: trunk/scipy/sandbox/maskedarray/setup.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/setup.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/setup.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+__author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3473 $"
+__date__     = '$Date: 2007-10-29 08:18:13 -0700 (Mon, 29 Oct 2007) $'
+
+import os
+
+def configuration(parent_package='',top_path=None):
+    from numpy.distutils.misc_util import Configuration
+    config = Configuration('maskedarray',parent_package,top_path)
+    config.add_data_dir('tests')
+    return config
+
+if __name__ == "__main__":
+    from numpy.distutils.core import setup
+    #setup.update(nmasetup)
+    config = configuration(top_path='').todict()
+    setup(**config)

Added: trunk/scipy/sandbox/maskedarray/tests/test_core.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_core.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_core.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,1316 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for MaskedArray & subclassing.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_core.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import types
+
+import numpy
+import numpy.core.fromnumeric  as fromnumeric
+from scipy.testing import *
+from numpy import array as narray
+
+from scipy.sandbox.maskedarray.testutils import *
+
+import scipy.sandbox.maskedarray.core as coremodule
+from scipy.sandbox.maskedarray.core import *
+
+pi = numpy.pi
+
+#..............................................................................
+class TestMA(TestCase):
+    "Base test class for MaskedArrays."
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+        self.setUp()
+
+    def setUp (self):
+        "Base data definition."
+        x = narray([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.])
+        y = narray([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.])
+        a10 = 10.
+        m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
+        m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1]
+        xm = masked_array(x, mask=m1)
+        ym = masked_array(y, mask=m2)
+        z = narray([-.5, 0., .5, .8])
+        zm = masked_array(z, mask=[0,1,0,0])
+        xf = numpy.where(m1, 1.e+20, x)
+        xm.set_fill_value(1.e+20)
+        self.d = (x, y, a10, m1, m2, xm, ym, z, zm, xf)
+    #........................
+    def test_basic1d(self):
+        "Test of basic array creation and properties in 1 dimension."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        assert(not isMaskedArray(x))
+        assert(isMaskedArray(xm))
+        assert((xm-ym).filled(0).any())
+        fail_if_equal(xm.mask.astype(int_), ym.mask.astype(int_))
+        s = x.shape
+        assert_equal(numpy.shape(xm), s)
+        assert_equal(xm.shape, s)
+        assert_equal(xm.dtype, x.dtype)
+        assert_equal(zm.dtype, z.dtype)
+        assert_equal(xm.size , reduce(lambda x,y:x*y, s))
+        assert_equal(count(xm) , len(m1) - reduce(lambda x,y:x+y, m1))
+        assert_array_equal(xm, xf)
+        assert_array_equal(filled(xm, 1.e20), xf)
+        assert_array_equal(x, xm)
+    #........................
+    def test_basic2d(self):
+        "Test of basic array creation and properties in 2 dimensions."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        for s in [(4,3), (6,2)]:
+            x.shape = s
+            y.shape = s
+            xm.shape = s
+            ym.shape = s
+            xf.shape = s
+
+            assert(not isMaskedArray(x))
+            assert(isMaskedArray(xm))
+            assert_equal(shape(xm), s)
+            assert_equal(xm.shape, s)
+            assert_equal( xm.size , reduce(lambda x,y:x*y, s))
+            assert_equal( count(xm) , len(m1) - reduce(lambda x,y:x+y, m1))
+            assert_equal(xm, xf)
+            assert_equal(filled(xm, 1.e20), xf)
+            assert_equal(x, xm)
+    #........................
+    def test_basic_arithmetic (self):
+        "Test of basic arithmetic."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        a2d = array([[1,2],[0,4]])
+        a2dm = masked_array(a2d, [[0,0],[1,0]])
+        assert_equal(a2d * a2d, a2d * a2dm)
+        assert_equal(a2d + a2d, a2d + a2dm)
+        assert_equal(a2d - a2d, a2d - a2dm)
+        for s in [(12,), (4,3), (2,6)]:
+            x = x.reshape(s)
+            y = y.reshape(s)
+            xm = xm.reshape(s)
+            ym = ym.reshape(s)
+            xf = xf.reshape(s)
+            assert_equal(-x, -xm)
+            assert_equal(x + y, xm + ym)
+            assert_equal(x - y, xm - ym)
+            assert_equal(x * y, xm * ym)
+            assert_equal(x / y, xm / ym)
+            assert_equal(a10 + y, a10 + ym)
+            assert_equal(a10 - y, a10 - ym)
+            assert_equal(a10 * y, a10 * ym)
+            assert_equal(a10 / y, a10 / ym)
+            assert_equal(x + a10, xm + a10)
+            assert_equal(x - a10, xm - a10)
+            assert_equal(x * a10, xm * a10)
+            assert_equal(x / a10, xm / a10)
+            assert_equal(x**2, xm**2)
+            assert_equal(abs(x)**2.5, abs(xm) **2.5)
+            assert_equal(x**y, xm**ym)
+            assert_equal(numpy.add(x,y), add(xm, ym))
+            assert_equal(numpy.subtract(x,y), subtract(xm, ym))
+            assert_equal(numpy.multiply(x,y), multiply(xm, ym))
+            assert_equal(numpy.divide(x,y), divide(xm, ym))
+    #........................
+    def test_mixed_arithmetic(self):
+        "Tests mixed arithmetics."
+        na = narray([1])
+        ma = array([1])
+        self.failUnless(isinstance(na + ma, MaskedArray))
+        self.failUnless(isinstance(ma + na, MaskedArray))
+    #........................
+    def test_inplace_arithmetic(self):
+        """Test of inplace operations and rich comparisons"""
+        # addition
+        x = arange(10)
+        y = arange(10)
+        xm = arange(10)
+        xm[2] = masked
+        x += 1
+        assert_equal(x, y+1)
+        xm += 1
+        assert_equal(xm, y+1)
+        # subtraction
+        x = arange(10)
+        xm = arange(10)
+        xm[2] = masked
+        x -= 1
+        assert_equal(x, y-1)
+        xm -= 1
+        assert_equal(xm, y-1)
+        # multiplication
+        x = arange(10)*1.0
+        xm = arange(10)*1.0
+        xm[2] = masked
+        x *= 2.0
+        assert_equal(x, y*2)
+        xm *= 2.0
+        assert_equal(xm, y*2)
+        # division
+        x = arange(10)*2
+        xm = arange(10)*2
+        xm[2] = masked
+        x /= 2
+        assert_equal(x, y)
+        xm /= 2
+        assert_equal(xm, y)
+        # division, pt 2
+        x = arange(10)*1.0
+        xm = arange(10)*1.0
+        xm[2] = masked
+        x /= 2.0
+        assert_equal(x, y/2.0)
+        xm /= arange(10)
+        assert_equal(xm, ones((10,)))
+
+        x = arange(10).astype(float_)
+        xm = arange(10)
+        xm[2] = masked
+#        id1 = id(x.raw_data())
+        id1 = x.raw_data().ctypes.data
+        x += 1.
+#        assert id1 == id(x.raw_data())
+        assert (id1 == x.raw_data().ctypes.data)
+        assert_equal(x, y+1.)
+        # addition w/ array
+        x = arange(10, dtype=float_)
+        xm = arange(10, dtype=float_)
+        xm[2] = masked
+        m = xm.mask
+        a = arange(10, dtype=float_)
+        a[-1] = masked
+        x += a
+        xm += a
+        assert_equal(x,y+a)
+        assert_equal(xm,y+a)
+        assert_equal(xm.mask, mask_or(m,a.mask))
+        # subtraction w/ array
+        x = arange(10, dtype=float_)
+        xm = arange(10, dtype=float_)
+        xm[2] = masked
+        m = xm.mask
+        a = arange(10, dtype=float_)
+        a[-1] = masked
+        x -= a
+        xm -= a
+        assert_equal(x,y-a)
+        assert_equal(xm,y-a)
+        assert_equal(xm.mask, mask_or(m,a.mask))
+        # multiplication w/ array
+        x = arange(10, dtype=float_)
+        xm = arange(10, dtype=float_)
+        xm[2] = masked
+        m = xm.mask
+        a = arange(10, dtype=float_)
+        a[-1] = masked
+        x *= a
+        xm *= a
+        assert_equal(x,y*a)
+        assert_equal(xm,y*a)
+        assert_equal(xm.mask, mask_or(m,a.mask))
+        # division w/ array
+        x = arange(10, dtype=float_)
+        xm = arange(10, dtype=float_)
+        xm[2] = masked
+        m = xm.mask
+        a = arange(10, dtype=float_)
+        a[-1] = masked
+        x /= a
+        xm /= a
+        assert_equal(x,y/a)
+        assert_equal(xm,y/a)
+        assert_equal(xm.mask, mask_or(mask_or(m,a.mask), (a==0)))
+        #
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        z = xm/ym
+        assert_equal(z._mask, [1,1,1,0,0,1,1,0,0,0,1,1])
+        assert_equal(z._data, [0.2,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.])
+        xm = xm.copy()
+        xm /= ym
+        assert_equal(xm._mask, [1,1,1,0,0,1,1,0,0,0,1,1])
+        assert_equal(xm._data, [1/5.,1.,1./3.,-1.,-pi/2.,-1.,5.,1.,1.,1.,2.,1.])
+
+
+    #..........................
+    def test_scalararithmetic(self):
+        "Tests some scalar arithmetics on MaskedArrays."
+        xm = array(0, mask=1)
+        assert((1/array(0)).mask)
+        assert((1 + xm).mask)
+        assert((-xm).mask)
+        assert((-xm).mask)
+        assert(maximum(xm, xm).mask)
+        assert(minimum(xm, xm).mask)
+        assert(xm.filled().dtype is xm.data.dtype)
+        x = array(0, mask=0)
+        assert_equal(x.filled().ctypes.data, x.ctypes.data)
+        assert_equal(str(xm), str(masked_print_option))
+    #.........................
+    def test_basic_ufuncs (self):
+        "Test various functions such as sin, cos."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        assert_equal(numpy.cos(x), cos(xm))
+        assert_equal(numpy.cosh(x), cosh(xm))
+        assert_equal(numpy.sin(x), sin(xm))
+        assert_equal(numpy.sinh(x), sinh(xm))
+        assert_equal(numpy.tan(x), tan(xm))
+        assert_equal(numpy.tanh(x), tanh(xm))
+        assert_equal(numpy.sqrt(abs(x)), sqrt(xm))
+        assert_equal(numpy.log(abs(x)), log(xm))
+        assert_equal(numpy.log10(abs(x)), log10(xm))
+        assert_equal(numpy.exp(x), exp(xm))
+        assert_equal(numpy.arcsin(z), arcsin(zm))
+        assert_equal(numpy.arccos(z), arccos(zm))
+        assert_equal(numpy.arctan(z), arctan(zm))
+        assert_equal(numpy.arctan2(x, y), arctan2(xm, ym))
+        assert_equal(numpy.absolute(x), absolute(xm))
+        assert_equal(numpy.equal(x,y), equal(xm, ym))
+        assert_equal(numpy.not_equal(x,y), not_equal(xm, ym))
+        assert_equal(numpy.less(x,y), less(xm, ym))
+        assert_equal(numpy.greater(x,y), greater(xm, ym))
+        assert_equal(numpy.less_equal(x,y), less_equal(xm, ym))
+        assert_equal(numpy.greater_equal(x,y), greater_equal(xm, ym))
+        assert_equal(numpy.conjugate(x), conjugate(xm))
+    #........................
+    def test_count_func (self):
+        "Tests count"
+        ott = array([0.,1.,2.,3.], mask=[1,0,0,0])
+        assert( isinstance(count(ott), int))
+        assert_equal(3, count(ott))
+        assert_equal(1, count(1))
+        assert_equal(0, array(1,mask=[1]))
+        ott = ott.reshape((2,2))
+        assert isMaskedArray(count(ott,0))
+        assert isinstance(count(ott), types.IntType)
+        assert_equal(3, count(ott))
+        assert getmask(count(ott,0)) is nomask
+        assert_equal([1,2],count(ott,0))
+    #........................
+    def test_minmax_func (self):
+        "Tests minimum and maximum."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        xr = numpy.ravel(x) #max doesn't work if shaped
+        xmr = ravel(xm)
+        assert_equal(max(xr), maximum(xmr)) #true because of careful selection of data
+        assert_equal(min(xr), minimum(xmr)) #true because of careful selection of data
+        #
+        assert_equal(minimum([1,2,3],[4,0,9]), [1,0,3])
+        assert_equal(maximum([1,2,3],[4,0,9]), [4,2,9])
+        x = arange(5)
+        y = arange(5) - 2
+        x[3] = masked
+        y[0] = masked
+        assert_equal(minimum(x,y), where(less(x,y), x, y))
+        assert_equal(maximum(x,y), where(greater(x,y), x, y))
+        assert minimum(x) == 0
+        assert maximum(x) == 4
+        #
+        x = arange(4).reshape(2,2)
+        x[-1,-1] = masked
+        assert_equal(maximum(x), 2)
+
+    def test_minmax_methods(self):
+        "Additional tests on max/min"
+        (_, _, _, _, _, xm, _, _, _, _) = self.d
+        xm.shape = (xm.size,)
+        assert_equal(xm.max(), 10)
+        assert(xm[0].max() is masked)
+        assert(xm[0].max(0) is masked)
+        assert(xm[0].max(-1) is masked)
+        assert_equal(xm.min(), -10.)
+        assert(xm[0].min() is masked)
+        assert(xm[0].min(0) is masked)
+        assert(xm[0].min(-1) is masked)
+        assert_equal(xm.ptp(), 20.)
+        assert(xm[0].ptp() is masked)
+        assert(xm[0].ptp(0) is masked)
+        assert(xm[0].ptp(-1) is masked)
+    #........................
+    def test_addsumprod (self):
+        "Tests add, sum, product."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        assert_equal(numpy.add.reduce(x), add.reduce(x))
+        assert_equal(numpy.add.accumulate(x), add.accumulate(x))
+        assert_equal(4, sum(array(4),axis=0))
+        assert_equal(4, sum(array(4), axis=0))
+        assert_equal(numpy.sum(x,axis=0), sum(x,axis=0))
+        assert_equal(numpy.sum(filled(xm,0),axis=0), sum(xm,axis=0))
+        assert_equal(numpy.sum(x,0), sum(x,0))
+        assert_equal(numpy.product(x,axis=0), product(x,axis=0))
+        assert_equal(numpy.product(x,0), product(x,0))
+        assert_equal(numpy.product(filled(xm,1),axis=0), product(xm,axis=0))
+        s = (3,4)
+        x.shape = y.shape = xm.shape = ym.shape = s
+        if len(s) > 1:
+            assert_equal(numpy.concatenate((x,y),1), concatenate((xm,ym),1))
+            assert_equal(numpy.add.reduce(x,1), add.reduce(x,1))
+            assert_equal(numpy.sum(x,1), sum(x,1))
+            assert_equal(numpy.product(x,1), product(x,1))
+    #.........................
+    def test_concat(self):
+        "Tests concatenations."
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        # basic concatenation
+        assert_equal(numpy.concatenate((x,y)), concatenate((xm,ym)))
+        assert_equal(numpy.concatenate((x,y)), concatenate((x,y)))
+        assert_equal(numpy.concatenate((x,y)), concatenate((xm,y)))
+        assert_equal(numpy.concatenate((x,y,x)), concatenate((x,ym,x)))
+        # Concatenation along an axis
+        s = (3,4)
+        x.shape = y.shape = xm.shape = ym.shape = s
+        assert_equal(xm.mask, numpy.reshape(m1, s))
+        assert_equal(ym.mask, numpy.reshape(m2, s))
+        xmym = concatenate((xm,ym),1)
+        assert_equal(numpy.concatenate((x,y),1), xmym)
+        assert_equal(numpy.concatenate((xm.mask,ym.mask),1), xmym._mask)
+    #........................
+    def test_indexing(self):
+        "Tests conversions and indexing"
+        x1 = numpy.array([1,2,4,3])
+        x2 = array(x1, mask=[1,0,0,0])
+        x3 = array(x1, mask=[0,1,0,1])
+        x4 = array(x1)
+    # test conversion to strings
+        junk, garbage = str(x2), repr(x2)
+        assert_equal(numpy.sort(x1),sort(x2,endwith=False))
+    # tests of indexing
+        assert type(x2[1]) is type(x1[1])
+        assert x1[1] == x2[1]
+        assert x2[0] is masked
+        assert_equal(x1[2],x2[2])
+        assert_equal(x1[2:5],x2[2:5])
+        assert_equal(x1[:],x2[:])
+        assert_equal(x1[1:], x3[1:])
+        x1[2] = 9
+        x2[2] = 9
+        assert_equal(x1,x2)
+        x1[1:3] = 99
+        x2[1:3] = 99
+        assert_equal(x1,x2)
+        x2[1] = masked
+        assert_equal(x1,x2)
+        x2[1:3] = masked
+        assert_equal(x1,x2)
+        x2[:] = x1
+        x2[1] = masked
+        assert allequal(getmask(x2),array([0,1,0,0]))
+        x3[:] = masked_array([1,2,3,4],[0,1,1,0])
+        assert allequal(getmask(x3), array([0,1,1,0]))
+        x4[:] = masked_array([1,2,3,4],[0,1,1,0])
+        assert allequal(getmask(x4), array([0,1,1,0]))
+        assert allequal(x4, array([1,2,3,4]))
+        x1 = numpy.arange(5)*1.0
+        x2 = masked_values(x1, 3.0)
+        assert_equal(x1,x2)
+        assert allequal(array([0,0,0,1,0],MaskType), x2.mask)
+#FIXME: Well, eh, fill_value is now a property        assert_equal(3.0, x2.fill_value())
+        assert_equal(3.0, x2.fill_value)
+        x1 = array([1,'hello',2,3],object)
+        x2 = numpy.array([1,'hello',2,3],object)
+        s1 = x1[1]
+        s2 = x2[1]
+        assert_equal(type(s2), str)
+        assert_equal(type(s1), str)
+        assert_equal(s1, s2)
+        assert x1[1:1].shape == (0,)
+    #........................
+    def test_copy(self):
+        "Tests of some subtle points of copying and sizing."
+        n = [0,0,1,0,0]
+        m = make_mask(n)
+        m2 = make_mask(m)
+        assert(m is m2)
+        m3 = make_mask(m, copy=1)
+        assert(m is not m3)
+
+        x1 = numpy.arange(5)
+        y1 = array(x1, mask=m)
+        #assert( y1._data is x1)
+        assert_equal(y1._data.__array_interface__, x1.__array_interface__)
+        assert( allequal(x1,y1.raw_data()))
+        #assert( y1.mask is m)
+        assert_equal(y1._mask.__array_interface__, m.__array_interface__)
+
+        y1a = array(y1)
+        #assert( y1a.raw_data() is y1.raw_data())
+        assert( y1a._data.__array_interface__ == y1._data.__array_interface__)
+        assert( y1a.mask is y1.mask)
+
+        y2 = array(x1, mask=m)
+        #assert( y2.raw_data() is x1)
+        assert (y2._data.__array_interface__ == x1.__array_interface__)
+        #assert( y2.mask is m)
+        assert (y2._mask.__array_interface__ == m.__array_interface__)
+        assert( y2[2] is masked)
+        y2[2] = 9
+        assert( y2[2] is not masked)
+        #assert( y2.mask is not m)
+        assert (y2._mask.__array_interface__ != m.__array_interface__)
+        assert( allequal(y2.mask, 0))
+
+        y3 = array(x1*1.0, mask=m)
+        assert(filled(y3).dtype is (x1*1.0).dtype)
+
+        x4 = arange(4)
+        x4[2] = masked
+        y4 = resize(x4, (8,))
+        assert_equal(concatenate([x4,x4]), y4)
+        assert_equal(getmask(y4),[0,0,1,0,0,0,1,0])
+        y5 = repeat(x4, (2,2,2,2), axis=0)
+        assert_equal(y5, [0,0,1,1,2,2,3,3])
+        y6 = repeat(x4, 2, axis=0)
+        assert_equal(y5, y6)
+        y7 = x4.repeat((2,2,2,2), axis=0)
+        assert_equal(y5,y7)
+        y8 = x4.repeat(2,0)
+        assert_equal(y5,y8)
+
+        y9 = x4.copy()
+        assert_equal(y9._data, x4._data)
+        assert_equal(y9._mask, x4._mask)
+        #
+        x = masked_array([1,2,3], mask=[0,1,0])
+        # Copy is False by default
+        y = masked_array(x)
+#        assert_equal(id(y._data), id(x._data))
+#        assert_equal(id(y._mask), id(x._mask))
+        assert_equal(y._data.ctypes.data, x._data.ctypes.data)
+        assert_equal(y._mask.ctypes.data, x._mask.ctypes.data)
+        y = masked_array(x, copy=True)
+#        assert_not_equal(id(y._data), id(x._data))
+#        assert_not_equal(id(y._mask), id(x._mask))
+        assert_not_equal(y._data.ctypes.data, x._data.ctypes.data)
+        assert_not_equal(y._mask.ctypes.data, x._mask.ctypes.data)
+    #........................
+    def test_where(self):
+        "Test the where function"
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        d = where(xm>2,xm,-9)
+        assert_equal(d, [-9.,-9.,-9.,-9., -9., 4., -9., -9., 10., -9., -9., 3.])
+        assert_equal(d._mask, xm._mask)
+        d = where(xm>2,-9,ym)
+        assert_equal(d, [5.,0.,3., 2., -1.,-9.,-9., -10., -9., 1., 0., -9.])
+        assert_equal(d._mask, [1,0,1,0,0,0,1,0,0,0,0,0])
+        d = where(xm>2, xm, masked)
+        assert_equal(d, [-9.,-9.,-9.,-9., -9., 4., -9., -9., 10., -9., -9., 3.])
+        tmp = xm._mask.copy()
+        tmp[(xm<=2).filled(True)] = True
+        assert_equal(d._mask, tmp)
+        #
+        ixm = xm.astype(int_)
+        d = where(ixm>2, ixm, masked)
+        assert_equal(d, [-9,-9,-9,-9, -9, 4, -9, -9, 10, -9, -9, 3])
+        assert_equal(d.dtype, ixm.dtype)
+        #
+        x = arange(10)
+        x[3] = masked
+        c = x >= 8
+        z = where(c , x, masked)
+        assert z.dtype is x.dtype
+        assert z[3] is masked
+        assert z[4] is masked
+        assert z[7] is masked
+        assert z[8] is not masked
+        assert z[9] is not masked
+        assert_equal(x,z)
+        #
+        z = where(c , masked, x)
+        assert z.dtype is x.dtype
+        assert z[3] is masked
+        assert z[4] is not masked
+        assert z[7] is not masked
+        assert z[8] is masked
+        assert z[9] is masked
+
+    #........................
+    def test_oddfeatures_1(self):
+        "Test of other odd features"
+        x = arange(20)
+        x = x.reshape(4,5)
+        x.flat[5] = 12
+        assert x[1,0] == 12
+        z = x + 10j * x
+        assert_equal(z.real, x)
+        assert_equal(z.imag, 10*x)
+        assert_equal((z*conjugate(z)).real, 101*x*x)
+        z.imag[...] = 0.0
+
+        x = arange(10)
+        x[3] = masked
+        assert str(x[3]) == str(masked)
+        c = x >= 8
+        assert count(where(c,masked,masked)) == 0
+        assert shape(where(c,masked,masked)) == c.shape
+        #
+        z = masked_where(c, x)
+        assert z.dtype is x.dtype
+        assert z[3] is masked
+        assert z[4] is not masked
+        assert z[7] is not masked
+        assert z[8] is masked
+        assert z[9] is masked
+        assert_equal(x,z)
+        #
+    #........................
+    def test_oddfeatures_2(self):
+        "Tests some more features."
+        x = array([1.,2.,3.,4.,5.])
+        c = array([1,1,1,0,0])
+        x[2] = masked
+        z = where(c, x, -x)
+        assert_equal(z, [1.,2.,0., -4., -5])
+        c[0] = masked
+        z = where(c, x, -x)
+        assert_equal(z, [1.,2.,0., -4., -5])
+        assert z[0] is masked
+        assert z[1] is not masked
+        assert z[2] is masked
+        #
+        x = arange(6)
+        x[5] = masked
+        y = arange(6)*10
+        y[2] = masked
+        c = array([1,1,1,0,0,0], mask=[1,0,0,0,0,0])
+        cm = c.filled(1)
+        z = where(c,x,y)
+        zm = where(cm,x,y)
+        assert_equal(z, zm)
+        assert getmask(zm) is nomask
+        assert_equal(zm, [0,1,2,30,40,50])
+        z = where(c, masked, 1)
+        assert_equal(z, [99,99,99,1,1,1])
+        z = where(c, 1, masked)
+        assert_equal(z, [99, 1, 1, 99, 99, 99])
+    #........................
+    def test_oddfeatures_3(self):
+        """Tests some generic features."""
+        atest = ones((10,10,10), dtype=float_)
+        btest = zeros(atest.shape, MaskType)
+        ctest = masked_where(btest,atest)
+        assert_equal(atest,ctest)
+    #........................
+    def test_maskingfunctions(self):
+        "Tests masking functions."
+        x = array([1.,2.,3.,4.,5.])
+        x[2] = masked
+        assert_equal(masked_where(greater(x, 2), x), masked_greater(x,2))
+        assert_equal(masked_where(greater_equal(x, 2), x), masked_greater_equal(x,2))
+        assert_equal(masked_where(less(x, 2), x), masked_less(x,2))
+        assert_equal(masked_where(less_equal(x, 2), x), masked_less_equal(x,2))
+        assert_equal(masked_where(not_equal(x, 2), x), masked_not_equal(x,2))
+        assert_equal(masked_where(equal(x, 2), x), masked_equal(x,2))
+        assert_equal(masked_where(not_equal(x,2), x), masked_not_equal(x,2))
+        assert_equal(masked_inside(range(5), 1, 3), [0, 199, 199, 199, 4])
+        assert_equal(masked_outside(range(5), 1, 3),[199,1,2,3,199])
+        assert_equal(masked_inside(array(range(5), mask=[1,0,0,0,0]), 1, 3).mask, [1,1,1,1,0])
+        assert_equal(masked_outside(array(range(5), mask=[0,1,0,0,0]), 1, 3).mask, [1,1,0,0,1])
+        assert_equal(masked_equal(array(range(5), mask=[1,0,0,0,0]), 2).mask, [1,0,1,0,0])
+        assert_equal(masked_not_equal(array([2,2,1,2,1], mask=[1,0,0,0,0]), 2).mask, [1,0,1,0,1])
+        assert_equal(masked_where([1,1,0,0,0], [1,2,3,4,5]), [99,99,3,4,5])
+    #........................
+    def test_TakeTransposeInnerOuter(self):
+        "Test of take, transpose, inner, outer products"
+        x = arange(24)
+        y = numpy.arange(24)
+        x[5:6] = masked
+        x = x.reshape(2,3,4)
+        y = y.reshape(2,3,4)
+        assert_equal(numpy.transpose(y,(2,0,1)), transpose(x,(2,0,1)))
+        assert_equal(numpy.take(y, (2,0,1), 1), take(x, (2,0,1), 1))
+        assert_equal(numpy.inner(filled(x,0),filled(y,0)),
+                            inner(x, y))
+        assert_equal(numpy.outer(filled(x,0),filled(y,0)),
+                            outer(x, y))
+        y = array(['abc', 1, 'def', 2, 3], object)
+        y[2] = masked
+        t = take(y,[0,3,4])
+        assert t[0] == 'abc'
+        assert t[1] == 2
+        assert t[2] == 3
+    #.......................
+    def test_maskedelement(self):
+        "Test of masked element"
+        x = arange(6)
+        x[1] = masked
+        assert(str(masked) ==  '--')
+        assert(x[1] is masked)
+        assert_equal(filled(x[1], 0), 0)
+        # don't know why these should raise an exception...
+        #self.failUnlessRaises(Exception, lambda x,y: x+y, masked, masked)
+        #self.failUnlessRaises(Exception, lambda x,y: x+y, masked, 2)
+        #self.failUnlessRaises(Exception, lambda x,y: x+y, masked, xx)
+        #self.failUnlessRaises(Exception, lambda x,y: x+y, xx, masked)
+    #........................
+    def test_scalar(self):
+        "Checks masking a scalar"
+        x = masked_array(0)
+        assert_equal(str(x), '0')
+        x = masked_array(0,mask=True)
+        assert_equal(str(x), str(masked_print_option))
+        x = masked_array(0, mask=False)
+        assert_equal(str(x), '0')
+    #........................
+    def test_usingmasked(self):
+        "Checks that there's no collapsing to masked"
+        x = masked_array([1,2])
+        y = x * masked
+        assert_equal(y.shape, x.shape)
+        assert_equal(y._mask, [True, True])
+        y = x[0] * masked
+        assert y is masked
+        y = x + masked
+        assert_equal(y.shape, x.shape)
+        assert_equal(y._mask, [True, True])
+
+    #........................
+    def test_topython(self):
+        "Tests some communication issues with Python."
+        assert_equal(1, int(array(1)))
+        assert_equal(1.0, float(array(1)))
+        assert_equal(1, int(array([[[1]]])))
+        assert_equal(1.0, float(array([[1]])))
+        self.failUnlessRaises(ValueError, float, array([1,1]))
+        assert numpy.isnan(float(array([1],mask=[1])))
+#TODO: Check how bool works...
+#TODO:        self.failUnless(bool(array([0,1])))
+#TODO:        self.failUnless(bool(array([0,0],mask=[0,1])))
+#TODO:        self.failIf(bool(array([0,0])))
+#TODO:        self.failIf(bool(array([0,0],mask=[0,0])))
+    #........................
+    def test_arraymethods(self):
+        "Tests some MaskedArray methods."
+        a = array([1,3,2])
+        b = array([1,3,2], mask=[1,0,1])
+        assert_equal(a.any(), a.data.any())
+        assert_equal(a.all(), a.data.all())
+        assert_equal(a.argmax(), a.data.argmax())
+        assert_equal(a.argmin(), a.data.argmin())
+        assert_equal(a.choose(0,1,2,3,4), a.data.choose(0,1,2,3,4))
+        assert_equal(a.compress([1,0,1]), a.data.compress([1,0,1]))
+        assert_equal(a.conj(), a.data.conj())
+        assert_equal(a.conjugate(), a.data.conjugate())
+        #
+        m = array([[1,2],[3,4]])
+        assert_equal(m.diagonal(), m.data.diagonal())
+        assert_equal(a.sum(), a.data.sum())
+        assert_equal(a.take([1,2]), a.data.take([1,2]))
+        assert_equal(m.transpose(), m.data.transpose())
+    #........................
+    def test_basicattributes(self):
+        "Tests some basic array attributes."
+        a = array([1,3,2])
+        b = array([1,3,2], mask=[1,0,1])
+        assert_equal(a.ndim, 1)
+        assert_equal(b.ndim, 1)
+        assert_equal(a.size, 3)
+        assert_equal(b.size, 3)
+        assert_equal(a.shape, (3,))
+        assert_equal(b.shape, (3,))
+    #........................
+    def test_single_element_subscript(self):
+        "Tests single element subscripts of Maskedarrays."
+        a = array([1,3,2])
+        b = array([1,3,2], mask=[1,0,1])
+        assert_equal(a[0].shape, ())
+        assert_equal(b[0].shape, ())
+        assert_equal(b[1].shape, ())
+    #........................
+    def test_maskcreation(self):
+        "Tests how masks are initialized at the creation of Maskedarrays."
+        data = arange(24, dtype=float_)
+        data[[3,6,15]] = masked
+        dma_1 = MaskedArray(data)
+        assert_equal(dma_1.mask, data.mask)
+        dma_2 = MaskedArray(dma_1)
+        assert_equal(dma_2.mask, dma_1.mask)
+        dma_3 = MaskedArray(dma_1, mask=[1,0,0,0]*6)
+        fail_if_equal(dma_3.mask, dma_1.mask)
+
+    def test_backwards(self):
+        "Tests backward compatibility with numpy.core.ma"
+        import numpy.core.ma as nma
+        x = nma.arange(5)
+        x[2] = nma.masked
+        X = masked_array(x, mask=x._mask)
+        assert_equal(X._mask, x.mask)
+        assert_equal(X._data, x._data)
+        X = masked_array(x)
+        assert_equal(X._data, x._data)
+        assert_equal(X._mask, x.mask)
+        assert_equal(getmask(x), [0,0,1,0,0])
+
+    def test_pickling(self):
+        "Tests pickling"
+        import cPickle
+        a = arange(10)
+        a[::3] = masked
+        a.fill_value = 999
+        a_pickled = cPickle.loads(a.dumps())
+        assert_equal(a_pickled._mask, a._mask)
+        assert_equal(a_pickled._data, a._data)
+        assert_equal(a_pickled.fill_value, 999)
+        #
+        a = array(numpy.matrix(range(10)), mask=[1,0,1,0,0]*2)
+        a_pickled = cPickle.loads(a.dumps())
+        assert_equal(a_pickled._mask, a._mask)
+        assert_equal(a_pickled, a)
+        assert(isinstance(a_pickled._data,numpy.matrix))
+    #
+    def test_fillvalue(self):
+        "Check that we don't lose the fill_value"
+        data = masked_array([1,2,3],fill_value=-999)
+        series = data[[0,2,1]]
+        assert_equal(series._fill_value, data._fill_value)
+    #
+    def test_asarray(self):
+        (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
+        xmm = asarray(xm)
+        assert_equal(xmm._data, xm._data)
+        assert_equal(xmm._mask, xm._mask)
+    #
+    def test_fix_invalid(self):
+        "Checks fix_invalid."
+        data = masked_array(numpy.sqrt([-1., 0., 1.]), mask=[0,0,1])
+        data_fixed = fix_invalid(data)
+        assert_equal(data_fixed._data, [data.fill_value, 0., 1.])
+        assert_equal(data_fixed._mask, [1., 0., 1.])
+    #
+    def test_imag_real(self):
+        xx = array([1+10j,20+2j], mask=[1,0])
+        assert_equal(xx.imag,[10,2])
+        assert_equal(xx.imag.filled(), [1e+20,2])
+        assert_equal(xx.imag.dtype, xx._data.imag.dtype)
+        assert_equal(xx.real,[1,20])
+        assert_equal(xx.real.filled(), [1e+20,20])
+        assert_equal(xx.real.dtype, xx._data.real.dtype)
+
+#...............................................................................
+
+class TestUfuncs(TestCase):
+    "Test class for the application of ufuncs on MaskedArrays."
+    def setUp(self):
+        "Base data definition."
+        self.d = (array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6),
+                  array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6),)
+
+    def test_testUfuncRegression(self):
+        "Tests new ufuncs on MaskedArrays."
+        for f in ['sqrt', 'log', 'log10', 'exp', 'conjugate',
+                  'sin', 'cos', 'tan',
+                  'arcsin', 'arccos', 'arctan',
+                  'sinh', 'cosh', 'tanh',
+                  'arcsinh',
+                  'arccosh',
+                  'arctanh',
+                  'absolute', 'fabs', 'negative',
+                  # 'nonzero', 'around',
+                  'floor', 'ceil',
+                  # 'sometrue', 'alltrue',
+                  'logical_not',
+                  'add', 'subtract', 'multiply',
+                  'divide', 'true_divide', 'floor_divide',
+                  'remainder', 'fmod', 'hypot', 'arctan2',
+                  'equal', 'not_equal', 'less_equal', 'greater_equal',
+                  'less', 'greater',
+                  'logical_and', 'logical_or', 'logical_xor',
+                  ]:
+            #print f
+            try:
+                uf = getattr(umath, f)
+            except AttributeError:
+                uf = getattr(fromnumeric, f)
+            mf = getattr(coremodule, f)
+            args = self.d[:uf.nin]
+            ur = uf(*args)
+            mr = mf(*args)
+            assert_equal(ur.filled(0), mr.filled(0), f)
+            assert_mask_equal(ur.mask, mr.mask)
+    #........................
+    def test_reduce(self):
+        "Tests reduce on MaskedArrays."
+        a = self.d[0]
+        assert(not alltrue(a,axis=0))
+        assert(sometrue(a,axis=0))
+        assert_equal(sum(a[:3],axis=0), 0)
+        assert_equal(product(a,axis=0), 0)
+        assert_equal(add.reduce(a), pi)
+    #........................
+    def test_minmax(self):
+        "Tests extrema on MaskedArrays."
+        a = arange(1,13).reshape(3,4)
+        amask = masked_where(a < 5,a)
+        assert_equal(amask.max(), a.max())
+        assert_equal(amask.min(), 5)
+        assert_equal(amask.max(0), a.max(0))
+        assert_equal(amask.min(0), [5,6,7,8])
+        assert(amask.max(1)[0].mask)
+        assert(amask.min(1)[0].mask)
+
+#...............................................................................
+
+class TestArrayMethods(TestCase):
+    "Test class for miscellaneous MaskedArrays methods."
+    def setUp(self):
+        "Base data definition."
+        x = numpy.array([ 8.375,  7.545,  8.828,  8.5  ,  1.757,  5.928,
+                      8.43 ,  7.78 ,  9.865,  5.878,  8.979,  4.732,
+                      3.012,  6.022,  5.095,  3.116,  5.238,  3.957,
+                      6.04 ,  9.63 ,  7.712,  3.382,  4.489,  6.479,
+                      7.189,  9.645,  5.395,  4.961,  9.894,  2.893,
+                      7.357,  9.828,  6.272,  3.758,  6.693,  0.993])
+        X = x.reshape(6,6)
+        XX = x.reshape(3,2,2,3)
+
+        m = numpy.array([0, 1, 0, 1, 0, 0,
+                     1, 0, 1, 1, 0, 1,
+                     0, 0, 0, 1, 0, 1,
+                     0, 0, 0, 1, 1, 1,
+                     1, 0, 0, 1, 0, 0,
+                     0, 0, 1, 0, 1, 0])
+        mx = array(data=x,mask=m)
+        mX = array(data=X,mask=m.reshape(X.shape))
+        mXX = array(data=XX,mask=m.reshape(XX.shape))
+
+        m2 = numpy.array([1, 1, 0, 1, 0, 0,
+                      1, 1, 1, 1, 0, 1,
+                      0, 0, 1, 1, 0, 1,
+                      0, 0, 0, 1, 1, 1,
+                      1, 0, 0, 1, 1, 0,
+                      0, 0, 1, 0, 1, 1])
+        m2x = array(data=x,mask=m2)
+        m2X = array(data=X,mask=m2.reshape(X.shape))
+        m2XX = array(data=XX,mask=m2.reshape(XX.shape))
+        self.d =  (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX)
+
+    #------------------------------------------------------
+    def test_trace(self):
+        "Tests trace on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        mXdiag = mX.diagonal()
+        assert_equal(mX.trace(), mX.diagonal().compressed().sum())
+        assert_almost_equal(mX.trace(),
+                            X.trace() - sum(mXdiag.mask*X.diagonal(),axis=0))
+
+    def test_clip(self):
+        "Tests clip on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        clipped = mx.clip(2,8)
+        assert_equal(clipped.mask,mx.mask)
+        assert_equal(clipped.data,x.clip(2,8))
+        assert_equal(clipped.data,mx.data.clip(2,8))
+
+    def test_ptp(self):
+        "Tests ptp on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        (n,m) = X.shape
+        assert_equal(mx.ptp(),mx.compressed().ptp())
+        rows = numpy.zeros(n,numpy.float_)
+        cols = numpy.zeros(m,numpy.float_)
+        for k in range(m):
+            cols[k] = mX[:,k].compressed().ptp()
+        for k in range(n):
+            rows[k] = mX[k].compressed().ptp()
+        assert_equal(mX.ptp(0),cols)
+        assert_equal(mX.ptp(1),rows)
+
+    def test_swapaxes(self):
+        "Tests swapaxes on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        mXswapped = mX.swapaxes(0,1)
+        assert_equal(mXswapped[-1],mX[:,-1])
+        mXXswapped = mXX.swapaxes(0,2)
+        assert_equal(mXXswapped.shape,(2,2,3,3))
+
+    def test_cumsumprod(self):
+        "Tests cumsum & cumprod on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        mXcp = mX.cumsum(0)
+        assert_equal(mXcp.data,mX.filled(0).cumsum(0))
+        mXcp = mX.cumsum(1)
+        assert_equal(mXcp.data,mX.filled(0).cumsum(1))
+        #
+        mXcp = mX.cumprod(0)
+        assert_equal(mXcp.data,mX.filled(1).cumprod(0))
+        mXcp = mX.cumprod(1)
+        assert_equal(mXcp.data,mX.filled(1).cumprod(1))
+
+    def test_varstd(self):
+        "Tests var & std on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        assert_almost_equal(mX.var(axis=None),mX.compressed().var())
+        assert_almost_equal(mX.std(axis=None),mX.compressed().std())
+        assert_equal(mXX.var(axis=3).shape,XX.var(axis=3).shape)
+        assert_equal(mX.var().shape,X.var().shape)
+        (mXvar0,mXvar1) = (mX.var(axis=0), mX.var(axis=1))
+        for k in range(6):
+            assert_almost_equal(mXvar1[k],mX[k].compressed().var())
+            assert_almost_equal(mXvar0[k],mX[:,k].compressed().var())
+            assert_almost_equal(numpy.sqrt(mXvar0[k]), mX[:,k].compressed().std())
+
+    def test_argmin(self):
+        "Tests argmin & argmax on MaskedArrays."
+        (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
+        #
+        assert_equal(mx.argmin(),35)
+        assert_equal(mX.argmin(),35)
+        assert_equal(m2x.argmin(),4)
+        assert_equal(m2X.argmin(),4)
+        assert_equal(mx.argmax(),28)
+        assert_equal(mX.argmax(),28)
+        assert_equal(m2x.argmax(),31)
+        assert_equal(m2X.argmax(),31)
+        #
+        assert_equal(mX.argmin(0), [2,2,2,5,0,5])
+        assert_equal(m2X.argmin(0), [2,2,4,5,0,4])
+        assert_equal(mX.argmax(0), [0,5,0,5,4,0])
+        assert_equal(m2X.argmax(0), [5,5,0,5,1,0])
+        #
+        assert_equal(mX.argmin(1), [4,1,0,0,5,5,])
+        assert_equal(m2X.argmin(1), [4,4,0,0,5,3])
+        assert_equal(mX.argmax(1), [2,4,1,1,4,1])
+        assert_equal(m2X.argmax(1), [2,4,1,1,1,1])
+
+    def test_put(self):
+        "Tests put."
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        x = array(d, mask = m)
+        assert( x[3] is masked)
+        assert( x[4] is masked)
+        x[[1,4]] = [10,40]
+#        assert( x.mask is not m)
+        assert( x[3] is masked)
+        assert( x[4] is not masked)
+        assert_equal(x, [0,10,2,-1,40])
+        #
+        x = masked_array(arange(10), mask=[1,0,0,0,0]*2)
+        i = [0,2,4,6]
+        x.put(i, [6,4,2,0])
+        assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,]))
+        assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0])
+        x.put(i, masked_array([0,2,4,6],[1,0,1,0]))
+        assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,])
+        assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0])
+        #
+        x = masked_array(arange(10), mask=[1,0,0,0,0]*2)
+        put(x, i, [6,4,2,0])
+        assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,]))
+        assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0])
+        put(x, i, masked_array([0,2,4,6],[1,0,1,0]))
+        assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,])
+        assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0])
+
+    def test_put_hardmask(self):
+        "Tests put on hardmask"
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        xh = array(d+1, mask = m, hard_mask=True, copy=True)
+        xh.put([4,2,0,1,3],[1,2,3,4,5])
+        assert_equal(xh._data, [3,4,2,4,5])
+
+    def test_take(self):
+        "Tests take"
+        x = masked_array([10,20,30,40],[0,1,0,1])
+        assert_equal(x.take([0,0,3]), masked_array([10, 10, 40], [0,0,1]) )
+        assert_equal(x.take([0,0,3]), x[[0,0,3]])
+        assert_equal(x.take([[0,1],[0,1]]),
+                     masked_array([[10,20],[10,20]], [[0,1],[0,1]]) )
+        #
+        x = array([[10,20,30],[40,50,60]], mask=[[0,0,1],[1,0,0,]])
+        assert_equal(x.take([0,2], axis=1),
+                     array([[10,30],[40,60]], mask=[[0,1],[1,0]]))
+        assert_equal(take(x, [0,2], axis=1),
+                      array([[10,30],[40,60]], mask=[[0,1],[1,0]]))
+        #........................
+    def test_anyall(self):
+        """Checks the any/all methods/functions."""
+        x = numpy.array([[ 0.13,  0.26,  0.90],
+                     [ 0.28,  0.33,  0.63],
+                     [ 0.31,  0.87,  0.70]])
+        m = numpy.array([[ True, False, False],
+                     [False, False, False],
+                     [True,  True, False]], dtype=numpy.bool_)
+        mx = masked_array(x, mask=m)
+        xbig = numpy.array([[False, False,  True],
+                        [False, False,  True],
+                        [False,  True,  True]], dtype=numpy.bool_)
+        mxbig = (mx > 0.5)
+        mxsmall = (mx < 0.5)
+        #
+        assert (mxbig.all()==False)
+        assert (mxbig.any()==True)
+        assert_equal(mxbig.all(0),[False, False, True])
+        assert_equal(mxbig.all(1), [False, False, True])
+        assert_equal(mxbig.any(0),[False, False, True])
+        assert_equal(mxbig.any(1), [True, True, True])
+        #
+        assert (mxsmall.all()==False)
+        assert (mxsmall.any()==True)
+        assert_equal(mxsmall.all(0), [True,   True, False])
+        assert_equal(mxsmall.all(1), [False, False, False])
+        assert_equal(mxsmall.any(0), [True,   True, False])
+        assert_equal(mxsmall.any(1), [True,   True, False])
+        #
+        X = numpy.matrix(x)
+        mX = masked_array(X, mask=m)
+        mXbig = (mX > 0.5)
+        mXsmall = (mX < 0.5)
+        #
+        assert (mXbig.all()==False)
+        assert (mXbig.any()==True)
+        assert_equal(mXbig.all(0), numpy.matrix([False, False, True]))
+        assert_equal(mXbig.all(1), numpy.matrix([False, False, True]).T)
+        assert_equal(mXbig.any(0), numpy.matrix([False, False, True]))
+        assert_equal(mXbig.any(1), numpy.matrix([ True,  True, True]).T)
+        #
+        assert (mXsmall.all()==False)
+        assert (mXsmall.any()==True)
+        assert_equal(mXsmall.all(0), numpy.matrix([True,   True, False]))
+        assert_equal(mXsmall.all(1), numpy.matrix([False, False, False]).T)
+        assert_equal(mXsmall.any(0), numpy.matrix([True,   True, False]))
+        assert_equal(mXsmall.any(1), numpy.matrix([True,   True, False]).T)
+
+    def test_keepmask(self):
+        "Tests the keep mask flag"
+        x = masked_array([1,2,3], mask=[1,0,0])
+        mx = masked_array(x)
+        assert_equal(mx.mask, x.mask)
+        mx = masked_array(x, mask=[0,1,0], keep_mask=False)
+        assert_equal(mx.mask, [0,1,0])
+        mx = masked_array(x, mask=[0,1,0], keep_mask=True)
+        assert_equal(mx.mask, [1,1,0])
+        # We default to true
+        mx = masked_array(x, mask=[0,1,0])
+        assert_equal(mx.mask, [1,1,0])
+
+    def test_hardmask(self):
+        "Test hard_mask"
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        xh = array(d, mask = m, hard_mask=True)
+        # We need to copy, to avoid updating d in xh!
+        xs = array(d, mask = m, hard_mask=False, copy=True)
+        xh[[1,4]] = [10,40]
+        xs[[1,4]] = [10,40]
+        assert_equal(xh._data, [0,10,2,3,4])
+        assert_equal(xs._data, [0,10,2,3,40])
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        assert_equal(xs.mask, [0,0,0,1,0])
+        assert(xh._hardmask)
+        assert(not xs._hardmask)
+        xh[1:4] = [10,20,30]
+        xs[1:4] = [10,20,30]
+        assert_equal(xh._data, [0,10,20,3,4])
+        assert_equal(xs._data, [0,10,20,30,40])
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        assert_equal(xs.mask, nomask)
+        xh[0] = masked
+        xs[0] = masked
+        assert_equal(xh.mask, [1,0,0,1,1])
+        assert_equal(xs.mask, [1,0,0,0,0])
+        xh[:] = 1
+        xs[:] = 1
+        assert_equal(xh._data, [0,1,1,3,4])
+        assert_equal(xs._data, [1,1,1,1,1])
+        assert_equal(xh.mask, [1,0,0,1,1])
+        assert_equal(xs.mask, nomask)
+        # Switch to soft mask
+        xh.soften_mask()
+        xh[:] = arange(5)
+        assert_equal(xh._data, [0,1,2,3,4])
+        assert_equal(xh.mask, nomask)
+        # Switch back to hard mask
+        xh.harden_mask()
+        xh[xh<3] = masked
+        assert_equal(xh._data, [0,1,2,3,4])
+        assert_equal(xh._mask, [1,1,1,0,0])
+        xh[filled(xh>1,False)] = 5
+        assert_equal(xh._data, [0,1,2,5,5])
+        assert_equal(xh._mask, [1,1,1,0,0])
+        #
+        xh = array([[1,2],[3,4]], mask = [[1,0],[0,0]], hard_mask=True)
+        xh[0] = 0
+        assert_equal(xh._data, [[1,0],[3,4]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        xh[-1,-1] = 5
+        assert_equal(xh._data, [[1,0],[3,5]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        xh[filled(xh<5,False)] = 2
+        assert_equal(xh._data, [[1,2],[2,5]])
+        assert_equal(xh._mask, [[1,0],[0,0]])
+        #
+        "Another test of hardmask"
+        d = arange(5)
+        n = [0,0,0,1,1]
+        m = make_mask(n)
+        xh = array(d, mask = m, hard_mask=True)
+        xh[4:5] = 999
+        #assert_equal(xh.mask.ctypes.data, m.ctypes.data)
+        xh[0:1] = 999
+        assert_equal(xh._data,[999,1,2,3,4])
+
+    def test_smallmask(self):
+        "Checks the behaviour of _smallmask"
+        a = arange(10)
+        a[1] = masked
+        a[1] = 1
+        assert_equal(a._mask, nomask)
+        a = arange(10)
+        a._smallmask = False
+        a[1] = masked
+        a[1] = 1
+        assert_equal(a._mask, zeros(10))
+
+
+    def test_sort(self):
+        "Test sort"
+        x = array([1,4,2,3],mask=[0,1,0,0],dtype=numpy.uint8)
+        #
+        sortedx = sort(x)
+        assert_equal(sortedx._data,[1,2,3,4])
+        assert_equal(sortedx._mask,[0,0,0,1])
+        #
+        sortedx = sort(x, endwith=False)
+        assert_equal(sortedx._data, [4,1,2,3])
+        assert_equal(sortedx._mask, [1,0,0,0])
+        #
+        x.sort()
+        assert_equal(x._data,[1,2,3,4])
+        assert_equal(x._mask,[0,0,0,1])
+        #
+        x = array([1,4,2,3],mask=[0,1,0,0],dtype=numpy.uint8)
+        x.sort(endwith=False)
+        assert_equal(x._data, [4,1,2,3])
+        assert_equal(x._mask, [1,0,0,0])
+        #
+        x = [1,4,2,3]
+        sortedx = sort(x)
+        assert(not isinstance(sorted, MaskedArray))
+        #
+        x = array([0,1,-1,-2,2], mask=nomask, dtype=numpy.int8)
+        sortedx = sort(x, endwith=False)
+        assert_equal(sortedx._data, [-2,-1,0,1,2])
+        x = array([0,1,-1,-2,2], mask=[0,1,0,0,1], dtype=numpy.int8)
+        sortedx = sort(x, endwith=False)
+        assert_equal(sortedx._data, [1,2,-2,-1,0])
+        assert_equal(sortedx._mask, [1,1,0,0,0])
+
+    def test_sort_2d(self):
+        "Check sort of 2D array."
+        # 2D array w/o mask
+        a = masked_array([[8,4,1],[2,0,9]])
+        a.sort(0)
+        assert_equal(a, [[2,0,1],[8,4,9]])
+        a = masked_array([[8,4,1],[2,0,9]])
+        a.sort(1)
+        assert_equal(a, [[1,4,8],[0,2,9]])
+        # 2D array w/mask
+        a = masked_array([[8,4,1],[2,0,9]], mask=[[1,0,0],[0,0,1]])
+        a.sort(0)
+        assert_equal(a, [[2,0,1],[8,4,9]])
+        assert_equal(a._mask, [[0,0,0],[1,0,1]])
+        a = masked_array([[8,4,1],[2,0,9]], mask=[[1,0,0],[0,0,1]])
+        a.sort(1)
+        assert_equal(a, [[1,4,8],[0,2,9]])
+        assert_equal(a._mask, [[0,0,1],[0,0,1]])
+        # 3D
+        a = masked_array([[[7, 8, 9],[4, 5, 6],[1, 2, 3]],
+                          [[1, 2, 3],[7, 8, 9],[4, 5, 6]],
+                          [[7, 8, 9],[1, 2, 3],[4, 5, 6]],
+                          [[4, 5, 6],[1, 2, 3],[7, 8, 9]]])
+        a[a%4==0] = masked
+        am = a.copy()
+        an = a.filled(99)
+        am.sort(0)
+        an.sort(0)
+        assert_equal(am, an)
+        am = a.copy()
+        an = a.filled(99)
+        am.sort(1)
+        an.sort(1)
+        assert_equal(am, an)
+        am = a.copy()
+        an = a.filled(99)
+        am.sort(2)
+        an.sort(2)
+        assert_equal(am, an)
+
+
+    def test_ravel(self):
+        "Tests ravel"
+        a = array([[1,2,3,4,5]], mask=[[0,1,0,0,0]])
+        aravel = a.ravel()
+        assert_equal(a._mask.shape, a.shape)
+        a = array([0,0], mask=[1,1])
+        aravel = a.ravel()
+        assert_equal(a._mask.shape, a.shape)
+        a = array(numpy.matrix([1,2,3,4,5]), mask=[[0,1,0,0,0]])
+        aravel = a.ravel()
+        assert_equal(a.shape,(1,5))
+        assert_equal(a._mask.shape, a.shape)
+        # Checs that small_mask is preserved
+        a = array([1,2,3,4],mask=[0,0,0,0],shrink=False)
+        assert_equal(a.ravel()._mask, [0,0,0,0])
+
+    def test_reshape(self):
+        "Tests reshape"
+        x = arange(4)
+        x[0] = masked
+        y = x.reshape(2,2)
+        assert_equal(y.shape, (2,2,))
+        assert_equal(y._mask.shape, (2,2,))
+        assert_equal(x.shape, (4,))
+        assert_equal(x._mask.shape, (4,))
+
+    def test_compressed(self):
+        "Tests compressed"
+        a = array([1,2,3,4],mask=[0,0,0,0])
+        b = a.compressed()
+        assert_equal(b, a)
+        assert_equal(b._mask, nomask)
+        a[0] = masked
+        b = a.compressed()
+        assert_equal(b._data, [2,3,4])
+        assert_equal(b._mask, nomask)
+
+    def test_tolist(self):
+        "Tests to list"
+        x = array(numpy.arange(12))
+        x[[1,-2]] = masked
+        xlist = x.tolist()
+        assert(xlist[1] is None)
+        assert(xlist[-2] is None)
+        #
+        x.shape = (3,4)
+        xlist = x.tolist()
+        #
+        assert_equal(xlist[0],[0,None,2,3])
+        assert_equal(xlist[1],[4,5,6,7])
+        assert_equal(xlist[2],[8,9,None,11])
+
+    def test_squeeze(self):
+        "Check squeeze"
+        data = masked_array([[1,2,3]])
+        assert_equal(data.squeeze(), [1,2,3])
+        data = masked_array([[1,2,3]], mask=[[1,1,1]])
+        assert_equal(data.squeeze(), [1,2,3])
+        assert_equal(data.squeeze()._mask, [1,1,1])
+        data = masked_array([[1]], mask=True)
+        assert(data.squeeze() is masked)
+
+#..............................................................................
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    unittest.main()

Added: trunk/scipy/sandbox/maskedarray/tests/test_extras.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_extras.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_extras.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,328 @@
+# pylint: disable-msg=W0611, W0612, W0511
+"""Tests suite for MaskedArray.
+Adapted from the original test_ma by Pierre Gerard-Marchant
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_extras.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import numpy as N
+from scipy.testing import *
+
+import scipy.sandbox.maskedarray.testutils
+from scipy.sandbox.maskedarray.testutils import *
+
+from scipy.sandbox.maskedarray.core import *
+from scipy.sandbox.maskedarray.extras import *
+
+class TestAverage(TestCase):
+    "Several tests of average. Why so many ? Good point..."
+    def test_testAverage1(self):
+        "Test of average."
+        ott = array([0.,1.,2.,3.], mask=[1,0,0,0])
+        assert_equal(2.0, average(ott,axis=0))
+        assert_equal(2.0, average(ott, weights=[1., 1., 2., 1.]))
+        result, wts = average(ott, weights=[1.,1.,2.,1.], returned=1)
+        assert_equal(2.0, result)
+        assert(wts == 4.0)
+        ott[:] = masked
+        assert_equal(average(ott,axis=0).mask, [True])
+        ott = array([0.,1.,2.,3.], mask=[1,0,0,0])
+        ott = ott.reshape(2,2)
+        ott[:,1] = masked
+        assert_equal(average(ott,axis=0), [2.0, 0.0])
+        assert_equal(average(ott,axis=1).mask[0], [True])
+        assert_equal([2.,0.], average(ott, axis=0))
+        result, wts = average(ott, axis=0, returned=1)
+        assert_equal(wts, [1., 0.])
+
+    def test_testAverage2(self):
+        "More tests of average."
+        w1 = [0,1,1,1,1,0]
+        w2 = [[0,1,1,1,1,0],[1,0,0,0,0,1]]
+        x = arange(6, dtype=float_)
+        assert_equal(average(x, axis=0), 2.5)
+        assert_equal(average(x, axis=0, weights=w1), 2.5)
+        y = array([arange(6, dtype=float_), 2.0*arange(6)])
+        assert_equal(average(y, None), N.add.reduce(N.arange(6))*3./12.)
+        assert_equal(average(y, axis=0), N.arange(6) * 3./2.)
+        assert_equal(average(y, axis=1), [average(x,axis=0), average(x,axis=0) * 2.0])
+        assert_equal(average(y, None, weights=w2), 20./6.)
+        assert_equal(average(y, axis=0, weights=w2), [0.,1.,2.,3.,4.,10.])
+        assert_equal(average(y, axis=1), [average(x,axis=0), average(x,axis=0) * 2.0])
+        m1 = zeros(6)
+        m2 = [0,0,1,1,0,0]
+        m3 = [[0,0,1,1,0,0],[0,1,1,1,1,0]]
+        m4 = ones(6)
+        m5 = [0, 1, 1, 1, 1, 1]
+        assert_equal(average(masked_array(x, m1),axis=0), 2.5)
+        assert_equal(average(masked_array(x, m2),axis=0), 2.5)
+        assert_equal(average(masked_array(x, m4),axis=0).mask, [True])
+        assert_equal(average(masked_array(x, m5),axis=0), 0.0)
+        assert_equal(count(average(masked_array(x, m4),axis=0)), 0)
+        z = masked_array(y, m3)
+        assert_equal(average(z, None), 20./6.)
+        assert_equal(average(z, axis=0), [0.,1.,99.,99.,4.0, 7.5])
+        assert_equal(average(z, axis=1), [2.5, 5.0])
+        assert_equal(average(z,axis=0, weights=w2), [0.,1., 99., 99., 4.0, 10.0])
+
+    def test_testAverage3(self):
+        "Yet more tests of average!"
+        a = arange(6)
+        b = arange(6) * 3
+        r1, w1 = average([[a,b],[b,a]], axis=1, returned=1)
+        assert_equal(shape(r1) , shape(w1))
+        assert_equal(r1.shape , w1.shape)
+        r2, w2 = average(ones((2,2,3)), axis=0, weights=[3,1], returned=1)
+        assert_equal(shape(w2) , shape(r2))
+        r2, w2 = average(ones((2,2,3)), returned=1)
+        assert_equal(shape(w2) , shape(r2))
+        r2, w2 = average(ones((2,2,3)), weights=ones((2,2,3)), returned=1)
+        assert_equal(shape(w2), shape(r2))
+        a2d = array([[1,2],[0,4]], float)
+        a2dm = masked_array(a2d, [[0,0],[1,0]])
+        a2da = average(a2d, axis=0)
+        assert_equal(a2da, [0.5, 3.0])
+        a2dma = average(a2dm, axis=0)
+        assert_equal(a2dma, [1.0, 3.0])
+        a2dma = average(a2dm, axis=None)
+        assert_equal(a2dma, 7./3.)
+        a2dma = average(a2dm, axis=1)
+        assert_equal(a2dma, [1.5, 4.0])
+
+class TestConcatenator(TestCase):
+    "Tests for mr_, the equivalent of r_ for masked arrays."
+    def test_1d(self):
+        "Tests mr_ on 1D arrays."
+        assert_array_equal(mr_[1,2,3,4,5,6],array([1,2,3,4,5,6]))
+        b = ones(5)
+        m = [1,0,0,0,0]
+        d = masked_array(b,mask=m)
+        c = mr_[d,0,0,d]
+        assert(isinstance(c,MaskedArray) or isinstance(c,core.MaskedArray))
+        assert_array_equal(c,[1,1,1,1,1,0,0,1,1,1,1,1])
+        assert_array_equal(c.mask, mr_[m,0,0,m])
+
+    def test_2d(self):
+        "Tests mr_ on 2D arrays."
+        a_1 = rand(5,5)
+        a_2 = rand(5,5)
+        m_1 = N.round_(rand(5,5),0)
+        m_2 = N.round_(rand(5,5),0)
+        b_1 = masked_array(a_1,mask=m_1)
+        b_2 = masked_array(a_2,mask=m_2)
+        d = mr_['1',b_1,b_2]  # append columns
+        assert(d.shape == (5,10))
+        assert_array_equal(d[:,:5],b_1)
+        assert_array_equal(d[:,5:],b_2)
+        assert_array_equal(d.mask, N.r_['1',m_1,m_2])
+        d = mr_[b_1,b_2]
+        assert(d.shape == (10,5))
+        assert_array_equal(d[:5,:],b_1)
+        assert_array_equal(d[5:,:],b_2)
+        assert_array_equal(d.mask, N.r_[m_1,m_2])
+
+class TestNotMasked(TestCase):
+    "Tests notmasked_edges and notmasked_contiguous."
+    def test_edges(self):
+        "Tests unmasked_edges"
+        a = masked_array(N.arange(24).reshape(3,8),
+                         mask=[[0,0,0,0,1,1,1,0],
+                               [1,1,1,1,1,1,1,1],
+                               [0,0,0,0,0,0,1,0],])
+        #
+        assert_equal(notmasked_edges(a, None), [0,23])
+        #
+        tmp = notmasked_edges(a, 0)
+        assert_equal(tmp[0], (array([0,0,0,0,2,2,0]), array([0,1,2,3,4,5,7])))
+        assert_equal(tmp[1], (array([2,2,2,2,2,2,2]), array([0,1,2,3,4,5,7])))
+        #
+        tmp = notmasked_edges(a, 1)
+        assert_equal(tmp[0], (array([0,2,]), array([0,0])))
+        assert_equal(tmp[1], (array([0,2,]), array([7,7])))
+
+    def test_contiguous(self):
+        "Tests notmasked_contiguous"
+        a = masked_array(N.arange(24).reshape(3,8),
+                         mask=[[0,0,0,0,1,1,1,1],
+                               [1,1,1,1,1,1,1,1],
+                               [0,0,0,0,0,0,1,0],])
+        tmp = notmasked_contiguous(a, None)
+        assert_equal(tmp[-1], slice(23,23,None))
+        assert_equal(tmp[-2], slice(16,21,None))
+        assert_equal(tmp[-3], slice(0,3,None))
+        #
+        tmp = notmasked_contiguous(a, 0)
+        assert(len(tmp[-1]) == 1)
+        assert(tmp[-2] is None)
+        assert_equal(tmp[-3],tmp[-1])
+        assert(len(tmp[0]) == 2)
+        #
+        tmp = notmasked_contiguous(a, 1)
+        assert_equal(tmp[0][-1], slice(0,3,None))
+        assert(tmp[1] is None)
+        assert_equal(tmp[2][-1], slice(7,7,None))
+        assert_equal(tmp[2][-2], slice(0,5,None))
+
+class Test2DFunctions(TestCase):
+    "Tests 2D functions"
+    def test_compress2d(self):
+        "Tests compress2d"
+        x = array(N.arange(9).reshape(3,3), mask=[[1,0,0],[0,0,0],[0,0,0]])
+        assert_equal(compress_rowcols(x), [[4,5],[7,8]] )
+        assert_equal(compress_rowcols(x,0), [[3,4,5],[6,7,8]] )
+        assert_equal(compress_rowcols(x,1), [[1,2],[4,5],[7,8]] )
+        x = array(x._data, mask=[[0,0,0],[0,1,0],[0,0,0]])
+        assert_equal(compress_rowcols(x), [[0,2],[6,8]] )
+        assert_equal(compress_rowcols(x,0), [[0,1,2],[6,7,8]] )
+        assert_equal(compress_rowcols(x,1), [[0,2],[3,5],[6,8]] )
+        x = array(x._data, mask=[[1,0,0],[0,1,0],[0,0,0]])
+        assert_equal(compress_rowcols(x), [[8]] )
+        assert_equal(compress_rowcols(x,0), [[6,7,8]] )
+        assert_equal(compress_rowcols(x,1,), [[2],[5],[8]] )
+        x = array(x._data, mask=[[1,0,0],[0,1,0],[0,0,1]])
+        assert_equal(compress_rowcols(x).size, 0 )
+        assert_equal(compress_rowcols(x,0).size, 0 )
+        assert_equal(compress_rowcols(x,1).size, 0 )
+    #
+    def test_mask_rowcols(self):
+        "Tests mask_rowcols."
+        x = array(N.arange(9).reshape(3,3), mask=[[1,0,0],[0,0,0],[0,0,0]])
+        assert_equal(mask_rowcols(x).mask, [[1,1,1],[1,0,0],[1,0,0]] )
+        assert_equal(mask_rowcols(x,0).mask, [[1,1,1],[0,0,0],[0,0,0]] )
+        assert_equal(mask_rowcols(x,1).mask, [[1,0,0],[1,0,0],[1,0,0]] )
+        x = array(x._data, mask=[[0,0,0],[0,1,0],[0,0,0]])
+        assert_equal(mask_rowcols(x).mask, [[0,1,0],[1,1,1],[0,1,0]] )
+        assert_equal(mask_rowcols(x,0).mask, [[0,0,0],[1,1,1],[0,0,0]] )
+        assert_equal(mask_rowcols(x,1).mask, [[0,1,0],[0,1,0],[0,1,0]] )
+        x = array(x._data, mask=[[1,0,0],[0,1,0],[0,0,0]])
+        assert_equal(mask_rowcols(x).mask, [[1,1,1],[1,1,1],[1,1,0]] )
+        assert_equal(mask_rowcols(x,0).mask, [[1,1,1],[1,1,1],[0,0,0]] )
+        assert_equal(mask_rowcols(x,1,).mask, [[1,1,0],[1,1,0],[1,1,0]] )
+        x = array(x._data, mask=[[1,0,0],[0,1,0],[0,0,1]])
+        assert(mask_rowcols(x).all())
+        assert(mask_rowcols(x,0).all())
+        assert(mask_rowcols(x,1).all())
+    #
+    def test_dot(self):
+        "Tests dot product"
+        n = N.arange(1,7)
+        #
+        m = [1,0,0,0,0,0]
+        a = masked_array(n, mask=m).reshape(2,3)
+        b = masked_array(n, mask=m).reshape(3,2)
+        c = dot(a,b,True)
+        assert_equal(c.mask, [[1,1],[1,0]])
+        c = dot(b,a,True)
+        assert_equal(c.mask, [[1,1,1],[1,0,0],[1,0,0]])
+        c = dot(a,b,False)
+        assert_equal(c, N.dot(a.filled(0), b.filled(0)))
+        c = dot(b,a,False)
+        assert_equal(c, N.dot(b.filled(0), a.filled(0)))
+        #
+        m = [0,0,0,0,0,1]
+        a = masked_array(n, mask=m).reshape(2,3)
+        b = masked_array(n, mask=m).reshape(3,2)
+        c = dot(a,b,True)
+        assert_equal(c.mask,[[0,1],[1,1]])
+        c = dot(b,a,True)
+        assert_equal(c.mask, [[0,0,1],[0,0,1],[1,1,1]])
+        c = dot(a,b,False)
+        assert_equal(c, N.dot(a.filled(0), b.filled(0)))
+        assert_equal(c, dot(a,b))
+        c = dot(b,a,False)
+        assert_equal(c, N.dot(b.filled(0), a.filled(0)))
+        #
+        m = [0,0,0,0,0,0]
+        a = masked_array(n, mask=m).reshape(2,3)
+        b = masked_array(n, mask=m).reshape(3,2)
+        c = dot(a,b)
+        assert_equal(c.mask,nomask)
+        c = dot(b,a)
+        assert_equal(c.mask,nomask)
+        #
+        a = masked_array(n, mask=[1,0,0,0,0,0]).reshape(2,3)
+        b = masked_array(n, mask=[0,0,0,0,0,0]).reshape(3,2)
+        c = dot(a,b,True)
+        assert_equal(c.mask,[[1,1],[0,0]])
+        c = dot(a,b,False)
+        assert_equal(c, N.dot(a.filled(0),b.filled(0)))
+        c = dot(b,a,True)
+        assert_equal(c.mask,[[1,0,0],[1,0,0],[1,0,0]])
+        c = dot(b,a,False)
+        assert_equal(c, N.dot(b.filled(0),a.filled(0)))
+        #
+        a = masked_array(n, mask=[0,0,0,0,0,1]).reshape(2,3)
+        b = masked_array(n, mask=[0,0,0,0,0,0]).reshape(3,2)
+        c = dot(a,b,True)
+        assert_equal(c.mask,[[0,0],[1,1]])
+        c = dot(a,b)
+        assert_equal(c, N.dot(a.filled(0),b.filled(0)))
+        c = dot(b,a,True)
+        assert_equal(c.mask,[[0,0,1],[0,0,1],[0,0,1]])
+        c = dot(b,a,False)
+        assert_equal(c, N.dot(b.filled(0), a.filled(0)))
+        #
+        a = masked_array(n, mask=[0,0,0,0,0,1]).reshape(2,3)
+        b = masked_array(n, mask=[0,0,1,0,0,0]).reshape(3,2)
+        c = dot(a,b,True)
+        assert_equal(c.mask,[[1,0],[1,1]])
+        c = dot(a,b,False)
+        assert_equal(c, N.dot(a.filled(0),b.filled(0)))
+        c = dot(b,a,True)
+        assert_equal(c.mask,[[0,0,1],[1,1,1],[0,0,1]])
+        c = dot(b,a,False)
+        assert_equal(c, N.dot(b.filled(0),a.filled(0)))
+
+    def test_mediff1d(self):
+        "Tests mediff1d"
+        x = masked_array(N.arange(5), mask=[1,0,0,0,1])
+        difx_d = (x._data[1:]-x._data[:-1])
+        difx_m = (x._mask[1:]-x._mask[:-1])
+        dx = mediff1d(x)
+        assert_equal(dx._data, difx_d)
+        assert_equal(dx._mask, difx_m)
+        #
+        dx = mediff1d(x, to_begin=masked)
+        assert_equal(dx._data, N.r_[0,difx_d])
+        assert_equal(dx._mask, N.r_[1,difx_m])
+        dx = mediff1d(x, to_begin=[1,2,3])
+        assert_equal(dx._data, N.r_[[1,2,3],difx_d])
+        assert_equal(dx._mask, N.r_[[0,0,0],difx_m])
+        #
+        dx = mediff1d(x, to_end=masked)
+        assert_equal(dx._data, N.r_[difx_d,0])
+        assert_equal(dx._mask, N.r_[difx_m,1])
+        dx = mediff1d(x, to_end=[1,2,3])
+        assert_equal(dx._data, N.r_[difx_d,[1,2,3]])
+        assert_equal(dx._mask, N.r_[difx_m,[0,0,0]])
+        #
+        dx = mediff1d(x, to_end=masked, to_begin=masked)
+        assert_equal(dx._data, N.r_[0,difx_d,0])
+        assert_equal(dx._mask, N.r_[1,difx_m,1])
+        dx = mediff1d(x, to_end=[1,2,3], to_begin=masked)
+        assert_equal(dx._data, N.r_[0,difx_d,[1,2,3]])
+        assert_equal(dx._mask, N.r_[1,difx_m,[0,0,0]])
+        #
+        dx = mediff1d(x._data, to_end=masked, to_begin=masked)
+        assert_equal(dx._data, N.r_[0,difx_d,0])
+        assert_equal(dx._mask, N.r_[1,0,0,0,0,1])
+
+class TestApplyAlongAxis(TestCase):
+    "Tests 2D functions"
+    def test_3d(self):
+        a = arange(12.).reshape(2,2,3)
+        def myfunc(b):
+            return b[1]
+        xa = apply_along_axis(myfunc,2,a)
+        assert_equal(xa,[[1,4],[7,10]])
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    unittest.main()

Added: trunk/scipy/sandbox/maskedarray/tests/test_morestats.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_morestats.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_morestats.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,109 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for maskedArray statistics.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_morestats.py 317 2007-10-04 19:31:14Z backtopop $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: backtopop $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 317 $"
+__date__     = '$Date: 2007-10-04 15:31:14 -0400 (Thu, 04 Oct 2007) $'
+
+import numpy
+from scipy.testing import *
+from scipy.sandbox.maskedarray import masked, masked_array
+import scipy.sandbox.maskedarray.mstats
+from scipy.sandbox.maskedarray.mstats import *
+from scipy.sandbox.maskedarray.morestats import *
+from scipy.sandbox.maskedarray.testutils import *
+
+
+class TestMisc(TestCase):
+    #
+    def __init__(self, *args, **kwargs):
+        TestCase.__init__(self, *args, **kwargs)
+    #
+    def test_mjci(self):
+        "Tests the Marits-Jarrett estimator"
+        data = masked_array([ 77, 87, 88,114,151,210,219,246,253,262,
+                             296,299,306,376,428,515,666,1310,2611])
+        assert_almost_equal(mjci(data),[55.76819,45.84028,198.8788],5)
+    #
+    def test_trimmedmeanci(self):
+        "Tests the confidence intervals of the trimmed mean."
+        data = masked_array([545,555,558,572,575,576,578,580,
+                             594,605,635,651,653,661,666])
+        assert_almost_equal(trimmed_mean(data,0.2), 596.2, 1)
+        assert_equal(numpy.round(trimmed_mean_ci(data,0.2),1), [561.8, 630.6])
+
+#..............................................................................
+class TestRanking(TestCase):
+    #
+    def __init__(self, *args, **kwargs):
+        TestCase.__init__(self, *args, **kwargs)
+    #
+    def test_ranking(self):
+        x = masked_array([0,1,1,1,2,3,4,5,5,6,])
+        assert_almost_equal(rank_data(x),[1,3,3,3,5,6,7,8.5,8.5,10])
+        x[[3,4]] = masked
+        assert_almost_equal(rank_data(x),[1,2.5,2.5,0,0,4,5,6.5,6.5,8])
+        assert_almost_equal(rank_data(x,use_missing=True),
+                            [1,2.5,2.5,4.5,4.5,4,5,6.5,6.5,8])
+        x = masked_array([0,1,5,1,2,4,3,5,1,6,])
+        assert_almost_equal(rank_data(x),[1,3,8.5,3,5,7,6,8.5,3,10])
+        x = masked_array([[0,1,1,1,2], [3,4,5,5,6,]])
+        assert_almost_equal(rank_data(x),[[1,3,3,3,5],[6,7,8.5,8.5,10]])
+        assert_almost_equal(rank_data(x,axis=1),[[1,3,3,3,5],[1,2,3.5,3.5,5]])
+        assert_almost_equal(rank_data(x,axis=0),[[1,1,1,1,1],[2,2,2,2,2,]])
+
+#..............................................................................
+class TestQuantiles(TestCase):
+    #
+    def __init__(self, *args, **kwargs):
+        TestCase.__init__(self, *args, **kwargs)
+    #
+    def test_hdquantiles(self):
+        data = [0.706560797,0.727229578,0.990399276,0.927065621,0.158953014,
+            0.887764025,0.239407086,0.349638551,0.972791145,0.149789972,
+            0.936947700,0.132359948,0.046041972,0.641675031,0.945530547,
+            0.224218684,0.771450991,0.820257774,0.336458052,0.589113496,
+            0.509736129,0.696838829,0.491323573,0.622767425,0.775189248,
+            0.641461450,0.118455200,0.773029450,0.319280007,0.752229111,
+            0.047841438,0.466295911,0.583850781,0.840581845,0.550086491,
+            0.466470062,0.504765074,0.226855960,0.362641207,0.891620942,
+            0.127898691,0.490094097,0.044882048,0.041441695,0.317976349,
+            0.504135618,0.567353033,0.434617473,0.636243375,0.231803616,
+            0.230154113,0.160011327,0.819464108,0.854706985,0.438809221,
+            0.487427267,0.786907310,0.408367937,0.405534192,0.250444460,
+            0.995309248,0.144389588,0.739947527,0.953543606,0.680051621,
+            0.388382017,0.863530727,0.006514031,0.118007779,0.924024803,
+            0.384236354,0.893687694,0.626534881,0.473051932,0.750134705,
+            0.241843555,0.432947602,0.689538104,0.136934797,0.150206859,
+            0.474335206,0.907775349,0.525869295,0.189184225,0.854284286,
+            0.831089744,0.251637345,0.587038213,0.254475554,0.237781276,
+            0.827928620,0.480283781,0.594514455,0.213641488,0.024194386,
+            0.536668589,0.699497811,0.892804071,0.093835427,0.731107772]
+        #
+        assert_almost_equal(hdquantiles(data,[0., 1.]),
+                            [0.006514031, 0.995309248])
+        hdq = hdquantiles(data,[0.25, 0.5, 0.75])
+        assert_almost_equal(hdq, [0.253210762, 0.512847491, 0.762232442,])
+        hdq = hdquantiles_sd(data,[0.25, 0.5, 0.75])
+        assert_almost_equal(hdq, [0.03786954, 0.03805389, 0.03800152,], 4)
+        #
+        data = numpy.array(data).reshape(10,10)
+        hdq = hdquantiles(data,[0.25,0.5,0.75],axis=0)
+        assert_almost_equal(hdq[:,0], hdquantiles(data[:,0],[0.25,0.5,0.75]))
+        assert_almost_equal(hdq[:,-1], hdquantiles(data[:,-1],[0.25,0.5,0.75]))
+        hdq = hdquantiles(data,[0.25,0.5,0.75],axis=0,var=True)
+        assert_almost_equal(hdq[...,0],
+                            hdquantiles(data[:,0],[0.25,0.5,0.75],var=True))
+        assert_almost_equal(hdq[...,-1],
+                            hdquantiles(data[:,-1],[0.25,0.5,0.75], var=True))
+
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    unittest.main()

Added: trunk/scipy/sandbox/maskedarray/tests/test_mrecords.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_mrecords.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_mrecords.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,176 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for mrecarray.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_mrecords.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import types
+
+import numpy as N
+import numpy.core.fromnumeric  as fromnumeric
+from scipy.testing import *
+
+from scipy.sandbox.maskedarray.testutils import *
+from scipy.sandbox import maskedarray
+from scipy.sandbox.maskedarray import masked_array, masked, nomask
+
+#import maskedarray.mrecords
+#from maskedarray.mrecords import mrecarray, fromarrays, fromtextfile, fromrecords
+from scipy.sandbox.maskedarray.mrecords import MaskedRecords, \
+    fromarrays, fromtextfile, fromrecords, addfield
+
+#..............................................................................
+class TestMRecords(TestCase):
+    "Base test class for MaskedArrays."
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+        self.setup()
+
+    def setup(self):
+        "Generic setup"
+        d = N.arange(5)
+        m = maskedarray.make_mask([1,0,0,1,1])
+        base_d = N.r_[d,d[::-1]].reshape(2,-1).T
+        base_m = N.r_[[m, m[::-1]]].T
+        base = masked_array(base_d, mask=base_m)
+        mrecord = fromarrays(base.T, dtype=[('a',N.float_),('b',N.float_)])
+        self.data = [d, m, mrecord]
+
+    def test_get(self):
+        "Tests fields retrieval"
+        [d, m, mrec] = self.data
+        mrec = mrec.copy()
+        assert_equal(mrec.a, masked_array(d,mask=m))
+        assert_equal(mrec.b, masked_array(d[::-1],mask=m[::-1]))
+        assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]], dtype=mrec._fieldmask.dtype)).all())
+        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+        assert_equal(mrec.a[1], mrec[1].a)
+        #
+        assert(isinstance(mrec[:2], MaskedRecords))
+        assert_equal(mrec[:2]['a'], d[:2])
+
+    def test_set(self):
+        "Tests setting fields/attributes."
+        [d, m, mrecord] = self.data
+        mrecord.a._data[:] = 5
+        assert_equal(mrecord['a']._data, [5,5,5,5,5])
+        mrecord.a = 1
+        assert_equal(mrecord['a']._data, [1]*5)
+        assert_equal(getmaskarray(mrecord['a']), [0]*5)
+        mrecord.b = masked
+        assert_equal(mrecord.b.mask, [1]*5)
+        assert_equal(getmaskarray(mrecord['b']), [1]*5)
+        mrecord._mask = masked
+        assert_equal(getmaskarray(mrecord['b']), [1]*5)
+        assert_equal(mrecord['a']._mask, mrecord['b']._mask)
+        mrecord._mask = nomask
+        assert_equal(getmaskarray(mrecord['b']), [0]*5)
+        assert_equal(mrecord['a']._mask, mrecord['b']._mask)
+        #
+    def test_setfields(self):
+        "Tests setting fields."
+        [d, m, mrecord] = self.data
+        mrecord.a[3:] = 5
+        assert_equal(mrecord.a, [0,1,2,5,5])
+        assert_equal(mrecord.a._mask, [1,0,0,0,0])
+        #
+        mrecord.b[3:] = masked
+        assert_equal(mrecord.b, [4,3,2,1,0])
+        assert_equal(mrecord.b._mask, [1,1,0,1,1])
+
+    def test_setslices(self):
+        "Tests setting slices."
+        [d, m, mrec] = self.data
+        mrec[:2] = 5
+        assert_equal(mrec.a._data, [5,5,2,3,4])
+        assert_equal(mrec.b._data, [5,5,2,1,0])
+        assert_equal(mrec.a._mask, [0,0,0,1,1])
+        assert_equal(mrec.b._mask, [0,0,0,0,1])
+        #
+        mrec[:2] = masked
+        assert_equal(mrec._mask, [1,1,0,0,1])
+        mrec[-2] = masked
+        assert_equal(mrec._mask, [1,1,0,1,1])
+        #
+    def test_setslices_hardmask(self):
+        "Tests setting slices w/ hardmask."
+        [d, m, mrec] = self.data
+        mrec.harden_mask()
+        mrec[-2:] = 5
+        assert_equal(mrec.a._data, [0,1,2,3,4])
+        assert_equal(mrec.b._data, [4,3,2,5,0])
+        assert_equal(mrec.a._mask, [1,0,0,1,1])
+        assert_equal(mrec.b._mask, [1,1,0,0,1])
+
+    def test_hardmask(self):
+        "Test hardmask"
+        [d, m, mrec] = self.data
+        mrec = mrec.copy()
+        mrec.harden_mask()
+        assert(mrec._hardmask)
+        mrec._mask = nomask
+        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+        mrec.soften_mask()
+        assert(not mrec._hardmask)
+        mrec._mask = nomask
+        assert(mrec['b']._mask is nomask)
+        assert_equal(mrec['a']._mask,mrec['b']._mask)
+
+    def test_fromrecords(self):
+        "Test from recarray."
+        [d, m, mrec] = self.data
+        nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]],
+                                         dtype=[('a',N.float_),('b',N.float_)])
+        #....................
+        mrecfr = fromrecords(nrec)
+        assert_equal(mrecfr.a, mrec.a)
+        assert_equal(mrecfr.dtype, mrec.dtype)
+        #....................
+        tmp = mrec[::-1] #.tolist()
+        mrecfr = fromrecords(tmp)
+        assert_equal(mrecfr.a, mrec.a[::-1])
+        #....................
+        mrecfr = fromrecords(nrec.tolist(), names=nrec.dtype.names)
+        assert_equal(mrecfr.a, mrec.a)
+        assert_equal(mrecfr.dtype, mrec.dtype)
+
+    def test_fromtextfile(self):
+        "Tests reading from a text file."
+        fcontent = """#
+'One (S)','Two (I)','Three (F)','Four (M)','Five (-)','Six (C)'
+'strings',1,1.0,'mixed column',,1
+'with embedded "double quotes"',2,2.0,1.0,,1
+'strings',3,3.0E5,3,,1
+'strings',4,-1e-10,,,1
+"""
+        import os
+        from datetime import datetime
+        fname = 'tmp%s' % datetime.now().strftime("%y%m%d%H%M%S%s")
+        f = open(fname, 'w')
+        f.write(fcontent)
+        f.close()
+        mrectxt = fromtextfile(fname,delimitor=',',varnames='ABCDEFG')
+        os.unlink(fname)
+        #
+        assert(isinstance(mrectxt, MaskedRecords))
+        assert_equal(mrectxt.F, [1,1,1,1])
+        assert_equal(mrectxt.E._mask, [1,1,1,1])
+        assert_equal(mrectxt.C, [1,2,3.e+5,-1e-10])
+
+    def test_addfield(self):
+        "Tests addfield"
+        [d, m, mrec] = self.data
+        mrec = addfield(mrec, masked_array(d+10, mask=m[::-1]))
+        assert_equal(mrec.f2, d+10)
+        assert_equal(mrec.f2._mask, m[::-1])
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    unittest.main()

Added: trunk/scipy/sandbox/maskedarray/tests/test_mstats.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_mstats.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_mstats.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,173 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for maskedArray statistics.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_mstats.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import numpy
+
+from scipy.testing import *
+from scipy.sandbox import maskedarray
+from scipy.sandbox.maskedarray import masked, masked_array
+
+from scipy.sandbox.maskedarray.testutils import *
+from scipy.sandbox.maskedarray.mstats import *
+
+#..............................................................................
+class TestQuantiles(TestCase):
+    "Base test class for MaskedArrays."
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+        self.a = maskedarray.arange(1,101)
+    #
+    def test_1d_nomask(self):
+        "Test quantiles 1D - w/o mask."
+        a = self.a
+        assert_almost_equal(mquantiles(a, alphap=1., betap=1.),
+                            [25.75, 50.5, 75.25])
+        assert_almost_equal(mquantiles(a, alphap=0, betap=1.),
+                            [25., 50., 75.])
+        assert_almost_equal(mquantiles(a, alphap=0.5, betap=0.5),
+                            [25.5, 50.5, 75.5])
+        assert_almost_equal(mquantiles(a, alphap=0., betap=0.),
+                            [25.25, 50.5, 75.75])
+        assert_almost_equal(mquantiles(a, alphap=1./3, betap=1./3),
+                            [25.41666667, 50.5, 75.5833333])
+        assert_almost_equal(mquantiles(a, alphap=3./8, betap=3./8),
+                            [25.4375, 50.5, 75.5625])
+        assert_almost_equal(mquantiles(a), [25.45, 50.5, 75.55])#
+    #
+    def test_1d_mask(self):
+        "Test quantiles 1D - w/ mask."
+        a = self.a
+        a[1::2] = masked
+        assert_almost_equal(mquantiles(a, alphap=1., betap=1.),
+                            [25.5, 50.0, 74.5])
+        assert_almost_equal(mquantiles(a, alphap=0, betap=1.),
+                            [24., 49., 74.])
+        assert_almost_equal(mquantiles(a, alphap=0.5, betap=0.5),
+                            [25., 50., 75.])
+        assert_almost_equal(mquantiles(a, alphap=0., betap=0.),
+                            [24.5, 50.0, 75.5])
+        assert_almost_equal(mquantiles(a, alphap=1./3, betap=1./3),
+                            [24.833333, 50.0, 75.166666])
+        assert_almost_equal(mquantiles(a, alphap=3./8, betap=3./8),
+                            [24.875, 50., 75.125])
+        assert_almost_equal(mquantiles(a), [24.9, 50., 75.1])
+    #
+    def test_2d_nomask(self):
+        "Test quantiles 2D - w/o mask."
+        a = self.a
+        b = maskedarray.resize(a, (100,100))
+        assert_almost_equal(mquantiles(b), [25.45, 50.5, 75.55])
+        assert_almost_equal(mquantiles(b, axis=0), maskedarray.resize(a,(3,100)))
+        assert_almost_equal(mquantiles(b, axis=1),
+                            maskedarray.resize([25.45, 50.5, 75.55], (100,3)))
+    #
+    def test_2d_mask(self):
+        "Test quantiles 2D - w/ mask."
+        a = self.a
+        a[1::2] = masked
+        b = maskedarray.resize(a, (100,100))
+        assert_almost_equal(mquantiles(b), [25., 50., 75.])
+        assert_almost_equal(mquantiles(b, axis=0), maskedarray.resize(a,(3,100)))
+        assert_almost_equal(mquantiles(b, axis=1),
+                            maskedarray.resize([24.9, 50., 75.1], (100,3)))
+
+class TestMedian(TestCase):
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+
+    def test_2d(self):
+        "Tests median w/ 2D"
+        (n,p) = (101,30)
+        x = masked_array(numpy.linspace(-1.,1.,n),)
+        x[:10] = x[-10:] = masked
+        z = masked_array(numpy.empty((n,p), dtype=numpy.float_))
+        z[:,0] = x[:]
+        idx = numpy.arange(len(x))
+        for i in range(1,p):
+            numpy.random.shuffle(idx)
+            z[:,i] = x[idx]
+        assert_equal(mmedian(z[:,0]), 0)
+        assert_equal(mmedian(z), numpy.zeros((p,)))
+
+    def test_3d(self):
+        "Tests median w/ 3D"
+        x = maskedarray.arange(24).reshape(3,4,2)
+        x[x%3==0] = masked
+        assert_equal(mmedian(x,0), [[12,9],[6,15],[12,9],[18,15]])
+        x.shape = (4,3,2)
+        assert_equal(mmedian(x,0),[[99,10],[11,99],[13,14]])
+        x = maskedarray.arange(24).reshape(4,3,2)
+        x[x%5==0] = masked
+        assert_equal(mmedian(x,0), [[12,10],[8,9],[16,17]])
+
+#..............................................................................
+class TestTrimming(TestCase):
+    #
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+    #
+    def test_trim(self):
+        "Tests trimming."
+        x = maskedarray.arange(100)
+        assert_equal(trim_both(x).count(), 60)
+        assert_equal(trim_tail(x,tail='r').count(), 80)
+        x[50:70] = masked
+        trimx = trim_both(x)
+        assert_equal(trimx.count(), 48)
+        assert_equal(trimx._mask, [1]*16 + [0]*34 + [1]*20 + [0]*14 + [1]*16)
+        x._mask = nomask
+        x.shape = (10,10)
+        assert_equal(trim_both(x).count(), 60)
+        assert_equal(trim_tail(x).count(), 80)
+    #
+    def test_trimmedmean(self):
+        "Tests the trimmed mean."
+        data = masked_array([ 77, 87, 88,114,151,210,219,246,253,262,
+                             296,299,306,376,428,515,666,1310,2611])
+        assert_almost_equal(trimmed_mean(data,0.1), 343, 0)
+        assert_almost_equal(trimmed_mean(data,0.2), 283, 0)
+    #
+    def test_trimmed_stde(self):
+        "Tests the trimmed mean standard error."
+        data = masked_array([ 77, 87, 88,114,151,210,219,246,253,262,
+                             296,299,306,376,428,515,666,1310,2611])
+        assert_almost_equal(trimmed_stde(data,0.2), 56.1, 1)
+    #
+    def test_winsorization(self):
+        "Tests the Winsorization of the data."
+        data = masked_array([ 77, 87, 88,114,151,210,219,246,253,262,
+                             296,299,306,376,428,515,666,1310,2611])
+        assert_almost_equal(winsorize(data).varu(), 21551.4, 1)
+        data[5] = masked
+        winsorized = winsorize(data)
+        assert_equal(winsorized.mask, data.mask)
+#..............................................................................
+
+class TestMisc(TestCase):
+    def __init__(self, *args, **kwds):
+        TestCase.__init__(self, *args, **kwds)
+
+    def test_cov(self):
+        "Tests the cov function."
+        x = masked_array([[1,2,3],[4,5,6]], mask=[[1,0,0],[0,0,0]])
+        c = cov(x[0])
+        assert_equal(c, (x[0].anom()**2).sum())
+        c = cov(x[1])
+        assert_equal(c, (x[1].anom()**2).sum()/2.)
+        c = cov(x)
+        assert_equal(c[1,0], (x[0].anom()*x[1].anom()).sum())
+
+
+###############################################################################
+#------------------------------------------------------------------------------
+if __name__ == "__main__":
+    unittest.main()

Added: trunk/scipy/sandbox/maskedarray/tests/test_subclassing.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/tests/test_subclassing.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/tests/test_subclassing.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,182 @@
+# pylint: disable-msg=W0611, W0612, W0511,R0201
+"""Tests suite for MaskedArray & subclassing.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: test_subclassing.py 3778 2008-01-04 01:29:56Z matthew.brett at gmail.com $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: matthew.brett at gmail.com $)"
+__version__ = '1.0'
+__revision__ = "$Revision: 3778 $"
+__date__     = '$Date: 2008-01-03 17:29:56 -0800 (Thu, 03 Jan 2008) $'
+
+import numpy as N
+import numpy.core.numeric as numeric
+
+from scipy.testing import *
+
+from scipy.sandbox.maskedarray.testutils import *
+
+import scipy.sandbox.maskedarray.core as coremodule
+from scipy.sandbox.maskedarray.core import *
+
+
+class SubArray(N.ndarray):
+    """Defines a generic N.ndarray subclass, that stores some metadata
+    in the  dictionary `info`."""
+    def __new__(cls,arr,info={}):
+        x = N.asanyarray(arr).view(cls)
+        x.info = info
+        return x
+    def __array_finalize__(self, obj):
+        self.info = getattr(obj,'info',{})
+        return
+    def __add__(self, other):
+        result = N.ndarray.__add__(self, other)
+        result.info.update({'added':result.info.pop('added',0)+1})
+        return result
+subarray = SubArray
+
+class MSubArray(SubArray,MaskedArray):
+    def __new__(cls, data, info=None, mask=nomask):
+        subarr = SubArray(data, info)
+        _data = MaskedArray.__new__(cls, data=subarr, mask=mask)
+        _data.info = subarr.info
+        return _data
+    def __array_finalize__(self,obj):
+        MaskedArray.__array_finalize__(self,obj)
+        SubArray.__array_finalize__(self, obj)
+        return
+    def _get_series(self):
+        _view = self.view(MaskedArray)
+        _view._sharedmask = False
+        return _view
+    _series = property(fget=_get_series)
+msubarray = MSubArray
+
+class MMatrix(MaskedArray, N.matrix,):
+    def __new__(cls, data, mask=nomask):
+        mat = N.matrix(data)
+        _data = MaskedArray.__new__(cls, data=mat, mask=mask)
+        return _data
+    def __array_finalize__(self,obj):
+        N.matrix.__array_finalize__(self, obj)
+        MaskedArray.__array_finalize__(self,obj)
+        return
+    def _get_series(self):
+        _view = self.view(MaskedArray)
+        _view._sharedmask = False
+        return _view
+    _series = property(fget=_get_series)
+mmatrix = MMatrix
+
+
+
+class TestSubclassing(TestCase):
+    """Test suite for masked subclasses of ndarray."""
+
+    def test_data_subclassing(self):
+        "Tests whether the subclass is kept."
+        x = N.arange(5)
+        m = [0,0,1,0,0]
+        xsub = SubArray(x)
+        xmsub = masked_array(xsub, mask=m)
+        assert isinstance(xmsub, MaskedArray)
+        assert_equal(xmsub._data, xsub)
+        assert isinstance(xmsub._data, SubArray)
+
+    def test_maskedarray_subclassing(self):
+        "Tests subclassing MaskedArray"
+        x = N.arange(5)
+        mx = mmatrix(x,mask=[0,1,0,0,0])
+        assert isinstance(mx._data, N.matrix)
+        "Tests masked_unary_operation"
+        assert isinstance(add(mx,mx), mmatrix)
+        assert isinstance(add(mx,x), mmatrix)
+        assert_equal(add(mx,x), mx+x)
+        assert isinstance(add(mx,mx)._data, N.matrix)
+        assert isinstance(add.outer(mx,mx), mmatrix)
+        "Tests masked_binary_operation"
+        assert isinstance(hypot(mx,mx), mmatrix)
+        assert isinstance(hypot(mx,x), mmatrix)
+
+    def test_attributepropagation(self):
+        x = array(arange(5), mask=[0]+[1]*4)
+        my = masked_array(subarray(x))
+        ym = msubarray(x)
+        #
+        z = (my+1)
+        assert isinstance(z,MaskedArray)
+        assert not isinstance(z, MSubArray)
+        assert isinstance(z._data, SubArray)
+        assert_equal(z._data.info, {})
+        #
+        z = (ym+1)
+        assert isinstance(z, MaskedArray)
+        assert isinstance(z, MSubArray)
+        assert isinstance(z._data, SubArray)
+        assert z._data.info['added'] > 0
+        #
+        ym._set_mask([1,0,0,0,1])
+        assert_equal(ym._mask, [1,0,0,0,1])
+        ym._series._set_mask([0,0,0,0,1])
+        assert_equal(ym._mask, [0,0,0,0,1])
+        #
+        xsub = subarray(x, info={'name':'x'})
+        mxsub = masked_array(xsub)
+        assert hasattr(mxsub, 'info')
+        assert_equal(mxsub.info, xsub.info)
+
+    def test_subclasspreservation(self):
+        "Checks that masked_array(...,subok=True) preserves the class."
+        x = N.arange(5)
+        m = [0,0,1,0,0]
+        xinfo = [(i,j) for (i,j) in zip(x,m)]
+        xsub = MSubArray(x, mask=m, info={'xsub':xinfo})
+        #
+        mxsub = masked_array(xsub, subok=False)
+        assert not isinstance(mxsub, MSubArray)
+        assert isinstance(mxsub, MaskedArray)
+        assert_equal(mxsub._mask, m)
+        #
+        mxsub = asarray(xsub)
+        assert not isinstance(mxsub, MSubArray)
+        assert isinstance(mxsub, MaskedArray)
+        assert_equal(mxsub._mask, m)
+        #
+        mxsub = masked_array(xsub, subok=True)
+        assert isinstance(mxsub, MSubArray)
+        assert_equal(mxsub.info, xsub.info)
+        assert_equal(mxsub._mask, xsub._mask)
+        #
+        mxsub = asanyarray(xsub)
+        assert isinstance(mxsub, MSubArray)
+        assert_equal(mxsub.info, xsub.info)
+        assert_equal(mxsub._mask, m)
+
+
+################################################################################
+if __name__ == '__main__':
+    unittest.main()
+    #
+    if 0:
+        x = array(arange(5), mask=[0]+[1]*4)
+        my = masked_array(subarray(x))
+        ym = msubarray(x)
+        #
+        z = (my+1)
+        assert isinstance(z,MaskedArray)
+        assert not isinstance(z, MSubArray)
+        assert isinstance(z._data, SubArray)
+        assert_equal(z._data.info, {})
+        #
+        z = (ym+1)
+        assert isinstance(z, MaskedArray)
+        assert isinstance(z, MSubArray)
+        assert isinstance(z._data, SubArray)
+        assert z._data.info['added'] > 0
+        #
+        ym._set_mask([1,0,0,0,1])
+        assert_equal(ym._mask, [1,0,0,0,1])
+        ym._series._set_mask([0,0,0,0,1])
+        assert_equal(ym._mask, [0,0,0,0,1])

Added: trunk/scipy/sandbox/maskedarray/testutils.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/testutils.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/testutils.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,220 @@
+"""Miscellaneous functions for testing masked arrays and subclasses
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: testutils.py 3529 2007-11-13 08:01:14Z jarrod.millman $
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
+__version__ = "1.0"
+__revision__ = "$Revision: 3529 $"
+__date__ = "$Date: 2007-11-13 00:01:14 -0800 (Tue, 13 Nov 2007) $"
+
+
+import numpy as N
+from numpy.core import ndarray
+from numpy.core.numerictypes import float_
+import numpy.core.umath as umath
+from numpy.testing import NumpyTest, NumpyTestCase
+from numpy.testing.utils import build_err_msg, rand
+
+import core
+from core import mask_or, getmask, getmaskarray, masked_array, nomask, masked
+from core import filled, equal, less
+
+#------------------------------------------------------------------------------
+def approx (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+    """Returns true if all components of a and b are equal subject to given tolerances.
+
+If fill_value is True, masked values considered equal. Otherwise, masked values
+are considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error atol comes into play for those elements of b that are very
+small or zero; it says how small a must be also.
+    """
+    m = mask_or(getmask(a), getmask(b))
+    d1 = filled(a)
+    d2 = filled(b)
+    if d1.dtype.char == "O" or d2.dtype.char == "O":
+        return N.equal(d1,d2).ravel()
+    x = filled(masked_array(d1, copy=False, mask=m), fill_value).astype(float_)
+    y = filled(masked_array(d2, copy=False, mask=m), 1).astype(float_)
+    d = N.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+    return d.ravel()
+#................................................
+def _assert_equal_on_sequences(actual, desired, err_msg=''):
+    "Asserts the equality of two non-array sequences."
+    assert_equal(len(actual),len(desired),err_msg)
+    for k in range(len(desired)):
+        assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k,err_msg))
+    return
+
+def assert_equal_records(a,b):
+    """Asserts that two records are equal. Pretty crude for now."""
+    assert_equal(a.dtype, b.dtype)
+    for f in a.dtype.names:
+        (af, bf) = (getattr(a,f), getattr(b,f))
+        if not (af is masked) and not (bf is masked):
+            assert_equal(getattr(a,f), getattr(b,f))
+    return
+
+def assert_equal(actual,desired,err_msg=''):
+    """Asserts that two items are equal.
+    """
+    # Case #1: dictionary .....
+    if isinstance(desired, dict):
+        assert isinstance(actual, dict), repr(type(actual))
+        assert_equal(len(actual),len(desired),err_msg)
+        for k,i in desired.items():
+            assert k in actual, repr(k)
+            assert_equal(actual[k], desired[k], 'key=%r\n%s' % (k,err_msg))
+        return
+    # Case #2: lists .....
+    if isinstance(desired, (list,tuple)) and isinstance(actual, (list,tuple)):
+        return _assert_equal_on_sequences(actual, desired, err_msg='')
+    if not (isinstance(actual, ndarray) or isinstance(desired, ndarray)):
+        msg = build_err_msg([actual, desired], err_msg,)
+        assert desired == actual, msg
+        return
+    # Case #4. arrays or equivalent
+    if ((actual is masked) and not (desired is masked)) or \
+        ((desired is masked) and not (actual is masked)):
+        msg = build_err_msg([actual, desired], err_msg, header='', names=('x', 'y'))
+        raise ValueError(msg)
+    actual = N.array(actual, copy=False, subok=True)
+    desired = N.array(desired, copy=False, subok=True)
+    if actual.dtype.char in "OS" and desired.dtype.char in "OS":
+        return _assert_equal_on_sequences(actual.tolist(),
+                                          desired.tolist(),
+                                          err_msg='')
+    return assert_array_equal(actual, desired, err_msg)
+#.............................
+def fail_if_equal(actual,desired,err_msg='',):
+    """Raises an assertion error if two items are equal.
+    """
+    if isinstance(desired, dict):
+        assert isinstance(actual, dict), repr(type(actual))
+        fail_if_equal(len(actual),len(desired),err_msg)
+        for k,i in desired.items():
+            assert k in actual, repr(k)
+            fail_if_equal(actual[k], desired[k], 'key=%r\n%s' % (k,err_msg))
+        return
+    if isinstance(desired, (list,tuple)) and isinstance(actual, (list,tuple)):
+        fail_if_equal(len(actual),len(desired),err_msg)
+        for k in range(len(desired)):
+            fail_if_equal(actual[k], desired[k], 'item=%r\n%s' % (k,err_msg))
+        return
+    if isinstance(actual, N.ndarray) or isinstance(desired, N.ndarray):
+        return fail_if_array_equal(actual, desired, err_msg)
+    msg = build_err_msg([actual, desired], err_msg)
+    assert desired != actual, msg
+assert_not_equal = fail_if_equal
+#............................
+def assert_almost_equal(actual,desired,decimal=7,err_msg=''):
+    """Asserts that two items are almost equal.
+    The test is equivalent to abs(desired-actual) < 0.5 * 10**(-decimal)
+    """
+    if isinstance(actual, N.ndarray) or isinstance(desired, N.ndarray):
+        return assert_array_almost_equal(actual, desired, decimal, err_msg)
+    msg = build_err_msg([actual, desired], err_msg)
+    assert round(abs(desired - actual),decimal) == 0, msg
+#............................
+def assert_array_compare(comparison, x, y, err_msg='', header='',
+                         fill_value=True):
+    """Asserts that a comparison relation between two masked arrays is satisfied
+    elementwise."""
+    xf = filled(x)
+    yf = filled(y)
+    m = mask_or(getmask(x), getmask(y))
+
+    x = masked_array(xf, copy=False, subok=False, mask=m).filled(fill_value)
+    y = masked_array(yf, copy=False, subok=False, mask=m).filled(fill_value)
+
+    if ((x is masked) and not (y is masked)) or \
+        ((y is masked) and not (x is masked)):
+        msg = build_err_msg([x, y], err_msg, header=header, names=('x', 'y'))
+        raise ValueError(msg)
+
+    if (x.dtype.char != "O") and (x.dtype.char != "S"):
+        x = x.astype(float_)
+        if isinstance(x, N.ndarray) and x.size > 1:
+            x[N.isnan(x)] = 0
+        elif N.isnan(x):
+            x = 0
+    if (y.dtype.char != "O") and (y.dtype.char != "S"):
+        y = y.astype(float_)
+        if isinstance(y, N.ndarray) and y.size > 1:
+            y[N.isnan(y)] = 0
+        elif N.isnan(y):
+            y = 0
+    try:
+        cond = (x.shape==() or y.shape==()) or x.shape == y.shape
+        if not cond:
+            msg = build_err_msg([x, y],
+                                err_msg
+                                + '\n(shapes %s, %s mismatch)' % (x.shape,
+                                                                  y.shape),
+                                header=header,
+                                names=('x', 'y'))
+            assert cond, msg
+        val = comparison(x,y)
+        if m is not nomask and fill_value:
+            val = masked_array(val, mask=m, copy=False)
+        if isinstance(val, bool):
+            cond = val
+            reduced = [0]
+        else:
+            reduced = val.ravel()
+            cond = reduced.all()
+            reduced = reduced.tolist()
+        if not cond:
+            match = 100-100.0*reduced.count(1)/len(reduced)
+            msg = build_err_msg([x, y],
+                                err_msg
+                                + '\n(mismatch %s%%)' % (match,),
+                                header=header,
+                                names=('x', 'y'))
+            assert cond, msg
+    except ValueError:
+        msg = build_err_msg([x, y], err_msg, header=header, names=('x', 'y'))
+        raise ValueError(msg)
+#............................
+def assert_array_equal(x, y, err_msg=''):
+    """Checks the elementwise equality of two masked arrays."""
+    assert_array_compare(equal, x, y, err_msg=err_msg,
+                         header='Arrays are not equal')
+##............................
+def fail_if_array_equal(x, y, err_msg=''):
+    "Raises an assertion error if two masked arrays are not equal (elementwise)."
+    def compare(x,y):
+
+        return (not N.alltrue(approx(x, y)))
+    assert_array_compare(compare, x, y, err_msg=err_msg,
+                         header='Arrays are not equal')
+#............................
+def assert_array_almost_equal(x, y, decimal=6, err_msg=''):
+    """Checks the elementwise equality of two masked arrays, up to a given
+    number of decimals."""
+    def compare(x, y):
+        "Returns the result of the loose comparison between x and y)."
+        return approx(x,y, rtol=10.**-decimal)
+    assert_array_compare(compare, x, y, err_msg=err_msg,
+                         header='Arrays are not almost equal')
+#............................
+def assert_array_less(x, y, err_msg=''):
+    "Checks that x is smaller than y elementwise."
+    assert_array_compare(less, x, y, err_msg=err_msg,
+                         header='Arrays are not less-ordered')
+#............................
+assert_close = assert_almost_equal
+#............................
+def assert_mask_equal(m1, m2):
+    """Asserts the equality of two masks."""
+    if m1 is nomask:
+        assert(m2 is nomask)
+    if m2 is nomask:
+        assert(m1 is nomask)
+    assert_array_equal(m1, m2)
+
+if __name__ == '__main__':
+    a = 12
+    assert_equal(a, masked)

Added: trunk/scipy/sandbox/maskedarray/timer_comparison.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/timer_comparison.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/timer_comparison.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,461 @@
+
+import timeit
+
+import numpy
+from numpy import int_, float_, bool_
+import numpy.core.fromnumeric as fromnumeric
+
+from numpy.testing.utils import build_err_msg, rand
+
+
+numpy.seterr(all='ignore')
+
+pi = numpy.pi
+
+class moduletester:
+    #-----------------------------------
+    def __init__(self, module):
+        self.module = module
+        self.allequal = module.allequal
+        self.arange = module.arange
+        self.array = module.array
+#        self.average =  module.average
+        self.concatenate = module.concatenate
+        self.count = module.count
+        self.equal = module.equal
+        self.filled = module.filled
+        self.getmask = module.getmask
+        self.getmaskarray = module.getmaskarray
+        self.id = id
+        self.inner = module.inner
+        self.make_mask = module.make_mask
+        self.masked = module.masked
+        self.masked_array = module.masked_array
+        self.masked_values = module.masked_values
+        self.mask_or = module.mask_or
+        self.nomask = module.nomask
+        self.ones = module.ones
+        self.outer = module.outer
+        self.repeat = module.repeat
+        self.resize = module.resize
+        self.sort = module.sort
+        self.take = module.take
+        self.transpose = module.transpose
+        self.zeros = module.zeros
+        self.MaskType = module.MaskType
+        try:
+            self.umath = module.umath
+        except AttributeError:
+            self.umath = module.core.umath
+        self.testnames = []
+    #........................
+    def assert_array_compare(self, comparison, x, y, err_msg='', header='',
+                         fill_value=True):
+        """Asserts that a comparison relation between two masked arrays is satisfied
+        elementwise."""
+        xf = self.filled(x)
+        yf = self.filled(y)
+        m = self.mask_or(self.getmask(x), self.getmask(y))
+
+        x = self.filled(self.masked_array(xf, mask=m), fill_value)
+        y = self.filled(self.masked_array(yf, mask=m), fill_value)
+        if (x.dtype.char != "O"):
+            x = x.astype(float_)
+            if isinstance(x, numpy.ndarray) and x.size > 1:
+                x[numpy.isnan(x)] = 0
+            elif numpy.isnan(x):
+                x = 0
+        if (y.dtype.char != "O"):
+            y = y.astype(float_)
+            if isinstance(y, numpy.ndarray) and y.size > 1:
+                y[numpy.isnan(y)] = 0
+            elif numpy.isnan(y):
+                y = 0
+        try:
+            cond = (x.shape==() or y.shape==()) or x.shape == y.shape
+            if not cond:
+                msg = build_err_msg([x, y],
+                                    err_msg
+                                    + '\n(shapes %s, %s mismatch)' % (x.shape,
+                                                                      y.shape),
+                                    header=header,
+                                    names=('x', 'y'))
+                assert cond, msg
+            val = comparison(x,y)
+            if m is not self.nomask and fill_value:
+                val = self.masked_array(val, mask=m)
+            if isinstance(val, bool):
+                cond = val
+                reduced = [0]
+            else:
+                reduced = val.ravel()
+                cond = reduced.all()
+                reduced = reduced.tolist()
+            if not cond:
+                match = 100-100.0*reduced.count(1)/len(reduced)
+                msg = build_err_msg([x, y],
+                                    err_msg
+                                    + '\n(mismatch %s%%)' % (match,),
+                                    header=header,
+                                    names=('x', 'y'))
+                assert cond, msg
+        except ValueError:
+            msg = build_err_msg([x, y], err_msg, header=header, names=('x', 'y'))
+            raise ValueError(msg)
+    #............................
+    def assert_array_equal(self, x, y, err_msg=''):
+        """Checks the elementwise equality of two masked arrays."""
+        self.assert_array_compare(self.equal, x, y, err_msg=err_msg,
+                                  header='Arrays are not equal')
+    #----------------------------------
+    def test_0(self):
+        "Tests creation"
+        x = numpy.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.])
+        m = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
+        xm = self.masked_array(x, mask=m)
+        xm[0]
+    #----------------------------------
+    def test_1(self):
+        "Tests creation"
+        x = numpy.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.])
+        y = numpy.array([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.])
+        a10 = 10.
+        m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
+        m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1]
+        xm = self.masked_array(x, mask=m1)
+        ym = self.masked_array(y, mask=m2)
+        z = numpy.array([-.5, 0., .5, .8])
+        zm = self.masked_array(z, mask=[0,1,0,0])
+        xf = numpy.where(m1, 1.e+20, x)
+        xm.set_fill_value(1.e+20)
+        #.....
+        assert((xm-ym).filled(0).any())
+        #fail_if_equal(xm.mask.astype(int_), ym.mask.astype(int_))
+        s = x.shape
+        assert(xm.size == reduce(lambda x,y:x*y, s))
+        assert(self.count(xm) == len(m1) - reduce(lambda x,y:x+y, m1))
+        #.....
+        for s in [(4,3), (6,2)]:
+            x.shape = s
+            y.shape = s
+            xm.shape = s
+            ym.shape = s
+            xf.shape = s
+
+            assert(self.count(xm) == len(m1) - reduce(lambda x,y:x+y, m1))
+    #----------------------------------
+    def test_2(self):
+        "Tests conversions and indexing"
+        x1 = numpy.array([1,2,4,3])
+        x2 = self.array(x1, mask=[1,0,0,0])
+        x3 = self.array(x1, mask=[0,1,0,1])
+        x4 = self.array(x1)
+    # test conversion to strings
+        junk, garbage = str(x2), repr(x2)
+#        assert_equal(numpy.sort(x1), self.sort(x2, fill_value=0))
+    # tests of indexing
+        assert type(x2[1]) is type(x1[1])
+        assert x1[1] == x2[1]
+#        assert self.allequal(x1[2],x2[2])
+#        assert self.allequal(x1[2:5],x2[2:5])
+#        assert self.allequal(x1[:],x2[:])
+#        assert self.allequal(x1[1:], x3[1:])
+        x1[2] = 9
+        x2[2] = 9
+        self.assert_array_equal(x1,x2)
+        x1[1:3] = 99
+        x2[1:3] = 99
+#        assert self.allequal(x1,x2)
+        x2[1] = self.masked
+#        assert self.allequal(x1,x2)
+        x2[1:3] = self.masked
+#        assert self.allequal(x1,x2)
+        x2[:] = x1
+        x2[1] = self.masked
+#        assert self.allequal(self.getmask(x2),self.array([0,1,0,0]))
+        x3[:] = self.masked_array([1,2,3,4],[0,1,1,0])
+#        assert self.allequal(self.getmask(x3), self.array([0,1,1,0]))
+        x4[:] = self.masked_array([1,2,3,4],[0,1,1,0])
+#        assert self.allequal(self.getmask(x4), self.array([0,1,1,0]))
+#        assert self.allequal(x4, self.array([1,2,3,4]))
+        x1 = numpy.arange(5)*1.0
+        x2 = self.masked_values(x1, 3.0)
+#        assert self.allequal(x1,x2)
+#        assert self.allequal(self.array([0,0,0,1,0], self.MaskType), x2.mask)
+        x1 = self.array([1,'hello',2,3],object)
+        x2 = numpy.array([1,'hello',2,3],object)
+        s1 = x1[1]
+        s2 = x2[1]
+        assert x1[1:1].shape == (0,)
+        # Tests copy-size
+        n = [0,0,1,0,0]
+        m = self.make_mask(n)
+        m2 = self.make_mask(m)
+        assert(m is m2)
+        m3 = self.make_mask(m, copy=1)
+        assert(m is not m3)
+
+    #----------------------------------
+    def test_3(self):
+        "Tests resize/repeat"
+        x4 = self.arange(4)
+        x4[2] = self.masked
+        y4 = self.resize(x4, (8,))
+        assert self.allequal(self.concatenate([x4,x4]), y4)
+        assert self.allequal(self.getmask(y4),[0,0,1,0,0,0,1,0])
+        y5 = self.repeat(x4, (2,2,2,2), axis=0)
+        self.assert_array_equal(y5, [0,0,1,1,2,2,3,3])
+        y6 = self.repeat(x4, 2, axis=0)
+        assert self.allequal(y5, y6)
+        y7 = x4.repeat((2,2,2,2), axis=0)
+        assert self.allequal(y5,y7)
+        y8 = x4.repeat(2,0)
+        assert self.allequal(y5,y8)
+
+    #----------------------------------
+    def test_4(self):
+        "Test of take, transpose, inner, outer products"
+        x = self.arange(24)
+        y = numpy.arange(24)
+        x[5:6] = self.masked
+        x = x.reshape(2,3,4)
+        y = y.reshape(2,3,4)
+        assert self.allequal(numpy.transpose(y,(2,0,1)), self.transpose(x,(2,0,1)))
+        assert self.allequal(numpy.take(y, (2,0,1), 1), self.take(x, (2,0,1), 1))
+        assert self.allequal(numpy.inner(self.filled(x,0), self.filled(y,0)),
+                            self.inner(x, y))
+        assert self.allequal(numpy.outer(self.filled(x,0), self.filled(y,0)),
+                            self.outer(x, y))
+        y = self.array(['abc', 1, 'def', 2, 3], object)
+        y[2] = self.masked
+        t = self.take(y,[0,3,4])
+        assert t[0] == 'abc'
+        assert t[1] == 2
+        assert t[2] == 3
+    #----------------------------------
+    def test_5(self):
+        "Tests inplace w/ scalar"
+
+        x = self.arange(10)
+        y = self.arange(10)
+        xm = self.arange(10)
+        xm[2] = self.masked
+        x += 1
+        assert self.allequal(x, y+1)
+        xm += 1
+        assert self.allequal(xm, y+1)
+
+        x = self.arange(10)
+        xm = self.arange(10)
+        xm[2] = self.masked
+        x -= 1
+        assert self.allequal(x, y-1)
+        xm -= 1
+        assert self.allequal(xm, y-1)
+
+        x = self.arange(10)*1.0
+        xm = self.arange(10)*1.0
+        xm[2] = self.masked
+        x *= 2.0
+        assert self.allequal(x, y*2)
+        xm *= 2.0
+        assert self.allequal(xm, y*2)
+
+        x = self.arange(10)*2
+        xm = self.arange(10)*2
+        xm[2] = self.masked
+        x /= 2
+        assert self.allequal(x, y)
+        xm /= 2
+        assert self.allequal(xm, y)
+
+        x = self.arange(10)*1.0
+        xm = self.arange(10)*1.0
+        xm[2] = self.masked
+        x /= 2.0
+        assert self.allequal(x, y/2.0)
+        xm /= self.arange(10)
+        self.assert_array_equal(xm, self.ones((10,)))
+
+        x = self.arange(10).astype(float_)
+        xm = self.arange(10)
+        xm[2] = self.masked
+        id1 = self.id(x.raw_data())
+        x += 1.
+        #assert id1 == self.id(x.raw_data())
+        assert self.allequal(x, y+1.)
+
+
+    def test_6(self):
+        "Tests inplace w/ array"
+
+        x = self.arange(10, dtype=float_)
+        y = self.arange(10)
+        xm = self.arange(10, dtype=float_)
+        xm[2] = self.masked
+        m = xm.mask
+        a = self.arange(10, dtype=float_)
+        a[-1] = self.masked
+        x += a
+        xm += a
+        assert self.allequal(x,y+a)
+        assert self.allequal(xm,y+a)
+        assert self.allequal(xm.mask, self.mask_or(m,a.mask))
+
+        x = self.arange(10, dtype=float_)
+        xm = self.arange(10, dtype=float_)
+        xm[2] = self.masked
+        m = xm.mask
+        a = self.arange(10, dtype=float_)
+        a[-1] = self.masked
+        x -= a
+        xm -= a
+        assert self.allequal(x,y-a)
+        assert self.allequal(xm,y-a)
+        assert self.allequal(xm.mask, self.mask_or(m,a.mask))
+
+        x = self.arange(10, dtype=float_)
+        xm = self.arange(10, dtype=float_)
+        xm[2] = self.masked
+        m = xm.mask
+        a = self.arange(10, dtype=float_)
+        a[-1] = self.masked
+        x *= a
+        xm *= a
+        assert self.allequal(x,y*a)
+        assert self.allequal(xm,y*a)
+        assert self.allequal(xm.mask, self.mask_or(m,a.mask))
+
+        x = self.arange(10, dtype=float_)
+        xm = self.arange(10, dtype=float_)
+        xm[2] = self.masked
+        m = xm.mask
+        a = self.arange(10, dtype=float_)
+        a[-1] = self.masked
+        x /= a
+        xm /= a
+
+    #----------------------------------
+    def test_7(self):
+        "Tests ufunc"
+        d = (self.array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6),
+             self.array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6),)
+        for f in ['sqrt', 'log', 'log10', 'exp', 'conjugate',
+#                  'sin', 'cos', 'tan',
+#                  'arcsin', 'arccos', 'arctan',
+#                  'sinh', 'cosh', 'tanh',
+#                  'arcsinh',
+#                  'arccosh',
+#                  'arctanh',
+#                  'absolute', 'fabs', 'negative',
+#                  # 'nonzero', 'around',
+#                  'floor', 'ceil',
+#                  # 'sometrue', 'alltrue',
+#                  'logical_not',
+#                  'add', 'subtract', 'multiply',
+#                  'divide', 'true_divide', 'floor_divide',
+#                  'remainder', 'fmod', 'hypot', 'arctan2',
+#                  'equal', 'not_equal', 'less_equal', 'greater_equal',
+#                  'less', 'greater',
+#                  'logical_and', 'logical_or', 'logical_xor',
+                  ]:
+            #print f
+            try:
+                uf = getattr(self.umath, f)
+            except AttributeError:
+                uf = getattr(fromnumeric, f)
+            mf = getattr(self.module, f)
+            args = d[:uf.nin]
+            ur = uf(*args)
+            mr = mf(*args)
+            self.assert_array_equal(ur.filled(0), mr.filled(0), f)
+            self.assert_array_equal(ur._mask, mr._mask)
+
+    #----------------------------------
+    def test_99(self):
+        # test average
+        ott = self.array([0.,1.,2.,3.], mask=[1,0,0,0])
+        self.assert_array_equal(2.0, self.average(ott,axis=0))
+        self.assert_array_equal(2.0, self.average(ott, weights=[1., 1., 2., 1.]))
+        result, wts = self.average(ott, weights=[1.,1.,2.,1.], returned=1)
+        self.assert_array_equal(2.0, result)
+        assert(wts == 4.0)
+        ott[:] = self.masked
+        assert(self.average(ott,axis=0) is self.masked)
+        ott = self.array([0.,1.,2.,3.], mask=[1,0,0,0])
+        ott = ott.reshape(2,2)
+        ott[:,1] = self.masked
+        self.assert_array_equal(self.average(ott,axis=0), [2.0, 0.0])
+        assert(self.average(ott,axis=1)[0] is self.masked)
+        self.assert_array_equal([2.,0.], self.average(ott, axis=0))
+        result, wts = self.average(ott, axis=0, returned=1)
+        self.assert_array_equal(wts, [1., 0.])
+        w1 = [0,1,1,1,1,0]
+        w2 = [[0,1,1,1,1,0],[1,0,0,0,0,1]]
+        x = self.arange(6)
+        self.assert_array_equal(self.average(x, axis=0), 2.5)
+        self.assert_array_equal(self.average(x, axis=0, weights=w1), 2.5)
+        y = self.array([self.arange(6), 2.0*self.arange(6)])
+        self.assert_array_equal(self.average(y, None), numpy.add.reduce(numpy.arange(6))*3./12.)
+        self.assert_array_equal(self.average(y, axis=0), numpy.arange(6) * 3./2.)
+        self.assert_array_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0])
+        self.assert_array_equal(self.average(y, None, weights=w2), 20./6.)
+        self.assert_array_equal(self.average(y, axis=0, weights=w2), [0.,1.,2.,3.,4.,10.])
+        self.assert_array_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0])
+        m1 = self.zeros(6)
+        m2 = [0,0,1,1,0,0]
+        m3 = [[0,0,1,1,0,0],[0,1,1,1,1,0]]
+        m4 = self.ones(6)
+        m5 = [0, 1, 1, 1, 1, 1]
+        self.assert_array_equal(self.average(self.masked_array(x, m1),axis=0), 2.5)
+        self.assert_array_equal(self.average(self.masked_array(x, m2),axis=0), 2.5)
+    #    assert(self.average(masked_array(x, m4),axis=0) is masked)
+        self.assert_array_equal(self.average(self.masked_array(x, m5),axis=0), 0.0)
+        self.assert_array_equal(self.count(self.average(self.masked_array(x, m4),axis=0)), 0)
+        z = self.masked_array(y, m3)
+        self.assert_array_equal(self.average(z, None), 20./6.)
+        self.assert_array_equal(self.average(z, axis=0), [0.,1.,99.,99.,4.0, 7.5])
+        self.assert_array_equal(self.average(z, axis=1), [2.5, 5.0])
+        self.assert_array_equal(self.average(z,axis=0, weights=w2), [0.,1., 99., 99., 4.0, 10.0])
+    #------------------------
+    def test_A(self):
+        x = self.arange(24)
+        y = numpy.arange(24)
+        x[5:6] = self.masked
+        x = x.reshape(2,3,4)
+
+
+################################################################################
+if __name__ == '__main__':
+
+    setup_base = "from __main__ import moduletester \n"\
+                 "import numpy\n" \
+                 "tester = moduletester(module)\n"
+    setup_old = "import numpy.core.ma as module\n"+setup_base
+    setup_new = "import maskedarray.core_ini as module\n"+setup_base
+    setup_cur = "import maskedarray.core as module\n"+setup_base
+#    setup_alt = "import maskedarray.core_alt as module\n"+setup_base
+#    setup_tmp = "import maskedarray.core_tmp as module\n"+setup_base
+
+    (nrepeat, nloop) = (10, 10)
+
+    if 1:
+        for i in range(1,8):
+            func = 'tester.test_%i()' % i
+            old = timeit.Timer(func, setup_old).repeat(nrepeat, nloop*10)
+            new = timeit.Timer(func, setup_new).repeat(nrepeat, nloop*10)
+            cur = timeit.Timer(func, setup_cur).repeat(nrepeat, nloop*10)
+#            alt = timeit.Timer(func, setup_alt).repeat(nrepeat, nloop*10)
+#            tmp = timeit.Timer(func, setup_tmp).repeat(nrepeat, nloop*10)
+            old = numpy.sort(old)
+            new = numpy.sort(new)
+            cur = numpy.sort(cur)
+#            alt = numpy.sort(alt)
+#            tmp = numpy.sort(tmp)
+            print "#%i" % i +50*'.'
+            print eval("moduletester.test_%i.__doc__" % i)
+            print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1])
+            print "core_ini     : %.3f - %.3f" % (new[0], new[1])
+            print "core_current : %.3f - %.3f" % (cur[0], cur[1])
+#            print "core_alt     : %.3f - %.3f" % (alt[0], alt[1])
+#            print "core_tmp     : %.3f - %.3f" % (tmp[0], tmp[1])

Added: trunk/scipy/sandbox/maskedarray/version.py
===================================================================
--- trunk/scipy/sandbox/maskedarray/version.py	2008-01-12 02:06:23 UTC (rev 3816)
+++ trunk/scipy/sandbox/maskedarray/version.py	2008-01-12 06:53:41 UTC (rev 3817)
@@ -0,0 +1,11 @@
+"""Version number"""
+
+version = '1.00'
+release = False
+
+if not release:
+    import core
+    import extras
+    revision = [core.__revision__.split(':')[-1][:-1].strip(),
+                extras.__revision__.split(':')[-1][:-1].strip(),]
+    version += '.dev%04i' % max([int(rev) for rev in revision])




More information about the Scipy-svn mailing list