# -*- coding: utf-8 -*-

from __future__ import division

import numpy as np

from ..utilities import match_args_return, strip_mask
from .constants import SSO, cp0, r1, Kelvin, sfac, uPS
from .constants import db2Pascal, gamma, P0, M_S, valence_factor
from .library import (entropy_part, entropy_part_zerop, gibbs, gibbs_pt0_pt0,
                      enthalpy_SSO_0_p, specvol_SSO_0_p)

from .library import SA_from_SP_Baltic, SP_from_SA_Baltic, SAAR

# This first set is moved over from absolute_salinity_sstar_ct,
# which is being absorbed into this module.
__all__ = ['CT_from_t',
           'SA_from_SP',
           'Sstar_from_SP',
           'Abs_Pressure_from_p',
           'CT_from_entropy',
           'CT_from_pt',
           'SA_Sstar_from_SP',
           'SA_from_Sstar',
           'SP_from_SA',
           'SP_from_SR',
           #'SP_from_Sstar',  # TODO
           'SR_from_SP',
           'Sstar_from_SA',
           'deltaSA_from_SP',  # TODO
           'depth_from_z',
           'entropy_from_CT',
           'entropy_from_pt',
           'entropy_from_t',
           'ionic_strength_from_SA',
           'molality_from_SA',
           'p_from_Abs_Pressure',
           'p_from_z',
           'pot_enthalpy_from_pt',
           'pt0_from_t',
           'pt_from_CT',
           'pt_from_entropy',
           'pt_from_t',
           't90_from_t48',
           't90_from_t68',
           't_from_CT',
           't_from_entropy',
           'z_from_depth',
           'z_from_p']  # TODO: Test with geo_strf_dyn_height != None

DEG2RAD = np.pi / 180.0
n0, n1, n2 = 0, 1, 2  # constants used as arguments to gibbs()


def check_input(SP, p, lon, lat):
    """
    Check for out of range values.
    """

    # Helper for the "from_SP" functions.
    lon, lat, p, SP = np.broadcast_arrays(lon, lat, p, SP, subok=True)

    cond1 = ((p < 100) & (SP > 120))
    cond2 = ((p >= 100) & (SP > 42))
    if cond1.any() or cond2.any():  # don't modify input array
        mask = np.ma.filled(cond1, False) | np.ma.filled(cond2, False)
        SP = np.ma.array(SP, mask=mask)

    lon = lon % 360

    # FIXME: If we do keep the checks below, they need to
    # be reformulated with ValueError('pressure out of range') etc.
    # The original also checks for 9999s--a fossil from old-time
    # Fortran days.

    # I don't think we need these here; if any such checking is
    # needed, it should not just be for the "from_SP" functions.
    if False:
        if ((p < -1.5) | (p > 12000)).any():
            raise Exception('Sstar_from_SP: pressure is out of range')
        if ((lon < 0) | (lon > 360)).any():
            raise Exception('Sstar_from_SP: longitude is out of range')
        if (np.abs(lat) > 90).any():
            raise Exception('Sstar_from_SP: latitude is out of range')

    SP = np.maximum(SP, 0)  # Works on masked array also.

    return SP, p, lon, lat


@match_args_return
def Abs_Pressure_from_p(p):
    """
    Calculates Absolute Pressure from sea pressure.  Note that Absolute
    Pressure is in Pa NOT dbar.

    Parameters
    ---------
    p : array_like
        sea pressure [dbar]

    Returns
    -------
    Absolute_Pressure : array_like
        Absolute Pressure [Pa]


    Examples
    --------
    >>> import gsw
    >>> gsw.Abs_Pressure_from_p([0, 500, 1000])
    array([   101325.,   5101325.,  10101325.])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
    of seawater - 2010: Calculation and use of thermodynamic properties.
    Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
    UNESCO (English), 196 pp. See Eqn. (2.2.1).
    """

    return p * db2Pascal + P0


@match_args_return
def CT_from_entropy(SA, entropy):
    """
    Calculates Conservative Temperature with entropy as an input variable.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    entropy : array_like
              specific entropy [J kg :sup:`-1` K :sup:`-1`]

    Returns
    -------
    CT : array_like
         Conservative Temperature [:math:`^\circ` C (ITS-90)]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> entropy = [400.3892, 395.4378, 319.8668, 146.7910, 98.6471, 62.7919]
    >>> gsw.CT_from_entropy(SA, entropy)
    array([ 28.80990279,  28.43919923,  22.78619927,  10.22619767,
             6.82719674,   4.32360295])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See appendix  A.10.
    """

    SA = np.maximum(SA, 0)
    pt = pt_from_entropy(SA, entropy)
    return CT_from_pt(SA, pt)


@match_args_return
def CT_from_pt(SA, pt):
    """
    Calculates Conservative Temperature of seawater from potential
    temperature (whose reference sea pressure is zero dbar).

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    pt : array_like
         potential temperature referenced to a sea pressure of zero dbar
         [:math:`^\circ` C (ITS-90)]

    Returns
    -------
    CT : array_like
         Conservative Temperature [:math:`^\circ` C (ITS-90)]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> pt = [28.7832, 28.4209, 22.7850, 10.2305, 6.8292, 4.3245]
    >>> gsw.CT_from_pt(SA, pt)
    array([ 28.80992302,  28.43914426,  22.78624661,  10.22616561,
             6.82718342,   4.32356518])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 3.3.
    """

    SA, pt, mask = strip_mask(SA, pt)

    pot_enthalpy = pot_enthalpy_from_pt(SA, pt)

    CT = pot_enthalpy / cp0

    return np.ma.array(CT, mask=mask, copy=False)


@match_args_return
def SA_Sstar_from_SP(SP, p, lon, lat):
    """
    TODO: docstring
    """

    # Note: with match_args_return, the variables inside
    # this function are masked arrays, so the outputs of
    # other functions called here are also masked arrays.

    SP, p, lon, lat = check_input(SP, p, lon, lat)

    saar, in_ocean = SAAR(p, lon, lat)
    SA = uPS * SP * (1 + saar)
    Sstar = uPS * SP * (1 - r1 * saar)

    SA_baltic = SA_from_SP_Baltic(SP, lon, lat)
    bmask = SA_baltic.mask
    if bmask is not np.ma.nomask and not bmask.all():
        inbaltic = ~bmask
        SA[inbaltic] = SA_baltic[inbaltic]
        Sstar[inbaltic] = SA_baltic[inbaltic]

    return SA, Sstar


@match_args_return
def SA_from_Sstar(Sstar, p, lon, lat):
    """
    TODO: docstring
    """
    # maybe add some input checking...

    saar, in_ocean = SAAR(p, lon, lat)
    SA = Sstar * (1 + saar) / (1.0 - r1 * saar)

    # In the Baltic Sea, SA = Sstar, and note that gsw_delta_SA returns zero
    # for dSA in the Baltic.

    return SA, in_ocean


@match_args_return
def SP_from_SA(SA, p, lon, lat):
    """
    TODO: docstring
    """
    # maybe add input checking...

    saar, in_ocean = SAAR(p, lon, lat)
    SP = (35 / 35.16504) * SA / (1.0 + saar)

    SP_baltic = SP_from_SA_Baltic(SA, lon, lat)
    bmask = SP_baltic.mask
    if bmask is not np.ma.nomask and not bmask.all():
        inbaltic = ~bmask
        SP[inbaltic] = SP_baltic[inbaltic]

    return SP, in_ocean


@match_args_return
def SP_from_SR(SR):
    """
    Calculates Practical Salinity from Reference Salinity.

    Parameters
    ---------
    SR : array_like
        Reference Salinity [g kg :sup:`-1`]

    Returns
    -------
    SP : array_like
        Practical Salinity (PSS-78) [unitless]

    Examples
    --------
    TODO

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.
    """

    return 1. / uPS * SR


#def SP_from_Sstar():
#    pass


@match_args_return
def SR_from_SP(SP):
    """
    Calculates Reference Salinity from Practical Salinity.

    Parameters
    ---------
    SP : array_like
        Practical Salinity (PSS-78) [unitless]

    Returns
    -------
    SR : array_like
        Reference Salinity [g kg :sup:`-1`]

    Examples
    --------
    TODO

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.
    """

    return uPS * SP


@match_args_return
def Sstar_from_SA(SA, p, lon, lat):
    """
    TODO: docstring
    """

    saar, in_ocean = SAAR(p, lon, lat)
    Sstar = SA * (1 - r1 * saar) / (1 + saar)
    # dSA is zero in Baltic
    return Sstar, in_ocean


@match_args_return
def CT_from_t(SA, t, p):
    """
    Calculates Conservative Temperature of seawater from in situ
    temperature.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    t : array_like
        in situ temperature [:math:`^\circ` C (ITS-90)]
    p : array_like
        pressure [dbar]

    Returns
    -------
    TODO

    Examples
    --------
    TODO

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 3.3.
    """

    # Find values that are out of range, set them to NaN.
    invalid = np.logical_and(p < 100, np.logical_or(t > 80, t < -12))
    t[invalid] = np.ma.masked

    invalid = np.logical_and(p >= 100, np.logical_or(t > 40, t < -12))
    t[invalid] = np.ma.masked

    pt0 = pt0_from_t(SA, t, p)
    CT = CT_from_pt(SA, pt0)

    return CT


@match_args_return
def SA_from_SP(SP, p, lon, lat):
    """Calculates Absolute Salinity from Practical Salinity.

    Parameters
    ----------
    SP : array_like
         salinity (PSS-78) [unitless]
    p : array_like
        pressure [dbar]
    lon : array_like
          decimal degrees east [0..+360] or [-180..+180]
    lat : array_like
          decimal degrees (+ve N, -ve S) [-90..+90]

    Returns
    -------
    SA : masked array
         Absolute salinity [g kg :sup:`-1`]

    Notes
    -----
    The mask is only set when the observation is well and truly on dry
    land; often the warning flag is not set until one is several hundred
    kilometers inland from the coast.

    Since SP is non-negative by definition, this function changes any negative
    input values of SP to be zero.

    Examples
    --------
    >>> import gsw
    >>> SP = [34.5487, 34.7275, 34.8605, 34.6810, 34.5680, 34.5600]
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> lon = 188
    >>> lat = 4
    >>> gsw.SA_from_SP(SP, p, lon, lat)
    array([ 34.71177834,  34.89152262,  35.02554486,  34.84722903,
            34.73662847,  34.73236307])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 2.5 and appendices A.4 and A.5.

    .. [2] McDougall, T.J., D.R. Jackett and F.J. Millero, 2010: An algorithm
       for estimating Absolute Salinity in the global ocean. Submitted to Ocean
       Science. A preliminary version is available at Ocean Sci. Discuss.,
       6, 215-242.
       http://www.ocean-sci-discuss.net/6/215/2009/osd-6-215-2009-print.pdf
    """

    SP, p, lon, lat = check_input(SP, p, lon, lat)

    SA = (SSO / 35) * SP * (1 + SAAR(p, lon, lat)[0])
    SA_baltic = SA_from_SP_Baltic(SP, lon, lat)

    # The following function (SAAR) finds SAAR in the non-Baltic parts of
    # the world ocean.  (Actually, this SAAR look-up table returns values
    # of zero in the Baltic Sea since SAAR in the Baltic is a function of SP,
    # not space.
    if SA_baltic is not None:
        SA[~SA_baltic.mask] = SA_baltic[~SA_baltic.mask]

    return SA


@match_args_return
def Sstar_from_SP(SP, p, lon, lat):
    """
    Calculates Preformed Salinity from Absolute Salinity.

    Parameters
    ----------
    SP : array_like
         salinity (PSS-78) [unitless]
    p : array_like
        pressure [dbar]
    lon : array_like
          decimal degrees east [0..+360] or [-180..+180]
    lat : array_like
          decimal degrees (+ve N, -ve S) [-90..+90]

    Returns
    -------
    Sstar : masked array
            Preformed Salinity [g kg :sup:`-1`]

    Notes
    -----
    The mask is only set when the observation is well and truly on dry
    land; often the warning flag is not set until one is several hundred
    kilometers inland from the coast.

    Since SP is non-negative by definition, this function changes any negative
    input values of SP to be zero.

    Examples
    --------
    >>> import gsw
    >>> SP = [34.5487, 34.7275, 34.8605, 34.6810, 34.5680, 34.5600]
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> lon = 188
    >>> lat = 4
    >>> gsw.Sstar_from_SP(SP, p, lon, lat)
    array([ 34.71155368,  34.8911614 ,  35.02465027,  34.84359314,
            34.729034  ,  34.71967596])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 2.5 and appendices A.4 and A.5.

    .. [2] McDougall, T.J., D.R. Jackett and F.J. Millero, 2010: An algorithm
       for estimating Absolute Salinity in the global ocean. Submitted to Ocean
       Science. A preliminary version is available at Ocean Sci. Discuss.,
       6, 215-242.
    """

    SP, p, lon, lat = check_input(SP, p, lon, lat)

    Sstar = (SSO / 35.0) * SP * (1.0 - r1 * SAAR(p, lon, lat)[0])

    # In the Baltic Sea, Sstar==SA.
    Sstar_baltic = SA_from_SP_Baltic(SP, lon, lat)

    # TODO: Create Baltic and non-Baltic test cases.
    if Sstar_baltic is not None:
        Sstar[~Sstar_baltic.mask] = Sstar_baltic[~Sstar_baltic.mask]

    return Sstar


@match_args_return
def deltaSA_from_SP(SP, p, lon, lat):
    """
     gsw_deltaSA_from_SP                             Absolute Salinity Anomaly
                                                       from Practical Salinity
    ==========================================================================

     USAGE:
      deltaSA = gsw_deltaSA_from_SP(SP,p,long,lat)

     DESCRIPTION:
      Calculates Absolute Salinity Anomaly from Practical Salinity.  Since SP
      is non-negative by definition, this function changes any negative input
      values of SP to be zero.

     INPUT:
      SP   =  Practical Salinity  (PSS-78)                        [ unitless ]
      p    =  sea pressure                                            [ dbar ]
             ( i.e. absolute pressure - 10.1325 dbar )
      long =  longitude in decimal degrees                      [ 0 ... +360 ]
                                                         or  [ -180 ... +180 ]
      lat  =  latitude in decimal degrees north                [ -90 ... +90 ]

      p, lat & long may have dimensions 1x1 or Mx1 or 1xN or MxN,
      where SP is MxN.

     OUTPUT:
      deltaSA  =  Absolute Salinity Anomaly                           [ g/kg ]

     AUTHOR:
      Trevor McDougall & Paul Barker                      [ help@teos-10.org ]

     VERSION NUMBER: 3.03 (29th April, 2013)

     REFERENCES:
      IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of
       seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.  Available from http://www.TEOS-10.org
        See section 2.5 and appendices A.4 and A.5 of this TEOS-10 Manual.

      McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and
       P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity.
       Ocean Science, 8, 1117-1128.
       http://www.ocean-sci.net/8/1117/2012/os-8-1117-2012.pdf

    """
    return SA_from_SP(SP, p, lon, lat) - SR_from_SP(SP)


def depth_from_z(z):
    """
    Calculates depth from height, z.  Note that in general height is
    negative in the ocean.

    Parameters
    ---------
    z : array_like
        height [m]

    Returns
    -------
    depth : array_like
        depth [m]
    """

    return -z


@match_args_return
def entropy_from_CT(SA, CT):
    """
    Calculates specific entropy of seawater.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    CT : array_like
         Conservative Temperature [:math:`^\circ` C (ITS-90)]

    Returns
    -------
    entropy : array_like
              specific entropy [J kg :sup:`-1` K :sup:`-1`]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> CT = [28.8099, 28.4392, 22.7862, 10.2262, 6.8272, 4.3236]
    >>> gsw.entropy_from_CT(SA, CT)
    array([ 400.38916315,  395.43781023,  319.86680989,  146.79103279,
             98.64714648,   62.79185763])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See appendix A.10.
    """

    SA = np.maximum(SA, 0)
    pt0 = pt_from_CT(SA, CT)
    return -gibbs(n0, n1, n0, SA, pt0, 0)


@match_args_return
def entropy_from_pt(SA, pt):
    """
    Calculates specific entropy of seawater.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    pt : array_like
         potential temperature [:math:`^\circ` C (ITS-90)]

    Returns
    -------
    entropy : array_like
              specific entropy [J kg :sup:`-1` K :sup:`-1`]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> pt = [28.7832, 28.4210, 22.7850, 10.2305, 6.8292, 4.3245]
    >>> gsw.entropy_from_pt(SA, pt)
    array([ 400.38946744,  395.43839949,  319.86743859,  146.79054828,
             98.64691006,   62.79135672])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See appendix A.10.
    """

    SA = np.maximum(SA, 0)
    return -gibbs(n0, n1, n0, SA, pt, 0)


@match_args_return
def entropy_from_t(SA, t, p):
    """
     gsw_entropy_from_t                          specific entropy of seawater
    ==========================================================================

     USAGE:
      entropy  =  gsw_entropy_from_t(SA,t,p)

     DESCRIPTION:
      Calculates specific entropy of seawater.

     INPUT:
      SA  =  Absolute Salinity                                        [ g/kg ]
      t   =  in-situ temperature (ITS-90)                            [ deg C ]
      p   =  sea pressure                                             [ dbar ]
             ( i.e. absolute pressure - 10.1325 dbar )

      SA & t need to have the same dimensions.
      p may have dimensions 1x1 or Mx1 or 1xN or MxN, where SA & t are MxN.

     OUTPUT:
      entropy  =  specific entropy                                [ J/(kg*K) ]

     AUTHOR:
      David Jackett, Trevor McDougall and Paul Barker     [ help@teos-10.org ]

     VERSION NUMBER: 3.03 (29th April, 2013)

     REFERENCES:
      IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of
       seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.  Available from http://www.TEOS-10.org
    """

    return -gibbs(n0, n1, n0, SA, t, p)


@match_args_return
def ionic_strength_from_SA(SA):
    """
    Calculates the ionic strength of seawater from Absolute Salinity.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]

    Returns
    -------
    ionic_strength : array_like
                     ionic strength of seawater [mol kg :sup:`-1`]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> gsw.ionic_strength_from_SA(SA)
    array([ 0.71298118,  0.71680567,  0.71966059,  0.71586272,  0.71350891,
            0.71341953])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See Table L.1.

    .. [2] Millero, F. J., R. Feistel, D. G. Wright, and T. J. McDougall, 2008:
       The composition of Standard Seawater and the definition of the
       Reference-Composition Salinity Scale, Deep-Sea Res. I, 55, 50-72.
       See Eqns. 5.9 and 5.12.
    """

    # Molality of seawater in mol kg :sup:`-1`
    molality = molality_from_SA(SA)

    return 0.5 * valence_factor * molality


@match_args_return
def molality_from_SA(SA):
    """
    Calculates the molality of seawater from Absolute Salinity.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]

    Returns
    -------
    molality : array_like
            seawater molality [mol kg :sup:`-1`]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> gsw.molality_from_SA(SA)
    array([ 1.14508476,  1.15122708,  1.15581223,  1.14971265,  1.14593231,
            1.14578877])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.
    """

    # Molality of seawater in mol kg :sup:`-1`.
    SA = np.maximum(SA, 0)
    molality = SA / (M_S * (1000 - SA))

    return molality


@match_args_return
def p_from_Abs_Pressure(Absolute_Pressure):
    """
    Calculates sea pressure from Absolute Pressure. Note that Absolute
    Pressure is in Pa NOT dbar.

    Parameters
    ---------
    Absolute_Pressure : array_like
                        Absolute Pressure [Pa]

    Returns
    -------
    p : array_like
        sea pressure [dbar]

    Examples
    --------
    TODO

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See Eqn. (2.2.1).
    """

    return (Absolute_Pressure - P0) * 1. / db2Pascal


@match_args_return
def p_from_z(z, lat, geo_strf_dyn_height=0):
    """
    Calculates sea pressure from height using computationally-efficient
    48-term expression for density, in terms of SA, CT and p (McDougall et al.,
    2011).  Dynamic height anomaly, geo_strf_dyn_height, if provided, must be
    computed with its pr=0 (the surface.)

    Parameters
    ----------
    z : array_like
        height [m]
    lat : array_like
          latitude in decimal degrees north [-90..+90]
    geo_strf_dyn_height : float, optional
                          dynamic height anomaly [ m :sup:`2` s :sup:`-2` ]
                          The reference pressure (p_ref) of geo_strf_dyn_height
                          must be zero (0) dbar.

    Returns
    -------
    p : array_like
        pressure [dbar]

    Examples
    --------
    >>> import gsw
    >>> z = [-10., -50., -125., -250., -600., -1000.]
    >>> lat = 4.
    >>> gsw.p_from_z(z, lat)
    array([   10.05572704,    50.28354425,   125.73185732,   251.54028663,
             604.2099135 ,  1007.9900587 ])
    >>> -gsw.z_from_p(gsw.p_from_z(z, lat), lat)
    array([  10.,   50.,  125.,  250.,  600., 1000.])

    Notes
    -----
    Height (z) is NEGATIVE in the ocean. Depth is -z. Depth is not used in the
    gibbs library.

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.

    .. [2] McDougall T.J., P.M. Barker, R. Feistel and D.R. Jackett, 2011: A
       computationally efficient 48-term expression for the density of seawater
       in terms of Conservative Temperature, and related properties of seawater.

    .. [3] Moritz (2000) Goedetic reference system 1980. J. Geodesy, 74,
       128-133.

    .. [4] Saunders, P. M., 1981: Practical conversion of pressure to depth.
       Journal of Physical Oceanography, 11, 573-574.
    """

    X = np.sin(lat * DEG2RAD)
    sin2 = X ** 2
    gs = 9.780327 * (1.0 + (5.2792e-3 + (2.32e-5 * sin2)) * sin2)

    # get the first estimate of p from Saunders (1981)
    c1 = 5.25e-3 * sin2 + 5.92e-3
    p = -2 * z / ((1 - c1) + np.sqrt((1 - c1) * (1 - c1) + 8.84e-6 * z))

    df_dp = db2Pascal * specvol_SSO_0_p(p)  # Initial value for f derivative.

    f = (enthalpy_SSO_0_p(p) + gs *
         (z - 0.5 * gamma * (z ** 2)) - geo_strf_dyn_height)

    p_old = p
    p = p_old - f / df_dp
    p_mid = 0.5 * (p + p_old)
    df_dp = db2Pascal * specvol_SSO_0_p(p_mid)
    p = p_old - f / df_dp

    # After this one iteration through this modified Newton-Raphson iterative
    # procedure, the remaining error in p is at computer machine precision,
    # being no more than 1.6e-10 dbar.

    return p


@match_args_return
def pot_enthalpy_from_pt(SA, pt):
    """
    Calculates the potential enthalpy of seawater from potential
    temperature (whose reference sea pressure is zero dbar).

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    pt : array_like
         potential temperature referenced to a sea pressure of zero dbar
         [:math:`^\circ` C (ITS-90)]

    Returns
    -------
    pot_enthalpy : array_like
                   potential enthalpy [J kg :sup:`-1`]

    Notes
    -----
    TODO

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> pt = [28.7832, 28.4209, 22.7850, 10.2305, 6.8292, 4.3245]
    >>> gsw.pot_enthalpy_from_pt(SA, pt)
    array([ 115005.40853458,  113525.30870246,   90959.68769935,
             40821.50280454,   27253.21472227,   17259.10131183])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 3.2.
    """

    SA, pt, mask = strip_mask(SA, pt)

    SA = np.maximum(SA, 0)

    x2 = sfac * SA
    x = np.sqrt(x2)
    y = pt * 0.025  # Normalize for F03 and F08

    pot_enthalpy = (61.01362420681071 + y * (168776.46138048015 +
    y * (-2735.2785605119625 + y * (2574.2164453821433 +
    y * (-1536.6644434977543 + y * (545.7340497931629 +
    (-50.91091728474331 - 18.30489878927802 * y) * y))))) +
    x2 * (268.5520265845071 + y * (-12019.028203559312 +
    y * (3734.858026725145 + y * (-2046.7671145057618 +
    y * (465.28655623826234 + (-0.6370820302376359 -
    10.650848542359153 * y) * y)))) +
    x * (937.2099110620707 + y * (588.1802812170108 +
    y * (248.39476522971285 + (-3.871557904936333 -
    2.6268019854268356 * y) * y)) +
    x * (-1687.914374187449 + x * (246.9598888781377 +
    x * (123.59576582457964 - 48.5891069025409 * x)) +
    y * (936.3206544460336 +
    y * (-942.7827304544439 + y * (369.4389437509002 +
    (-33.83664947895248 - 9.987880382780322 * y) * y)))))))

    # The above polynomial for pot_enthalpy is the full expression for
    # potential enthalpy in terms of SA and pt, obtained from the Gibbs function
    # as below.  It has simply collected like powers of x and y so that it is
    # computationally faster than calling the Gibbs function twice as is done in
    # the commented code below. When this code below is run, the results are
    # identical to calculating pot_enthalpy as above, to machine precision.

    # g000 = gibbs(n0, n0, n0, SA, pt, 0)
    # g010 = gibbs(n0, n1, n0, SA, pt, 0)
    # pot_enthalpy = g000 - (Kelvin + pt) * g010

    # This is the end of the alternative code
    # %timeit gsw.CT_from_pt(SA, pt)
    # 1000 loops, best of 3: 1.34 ms per loop <- calling gibbs
    # 1000 loops, best of 3: 254 us per loop <- standard

    return np.ma.array(pot_enthalpy, mask=mask, copy=False)


@match_args_return
def pt0_from_t(SA, t, p):
    """
    Calculates potential temperature with reference pressure, pr = 0 dbar.
    The present routine is computationally faster than the more general
    function "pt_from_t(SA, t, p, pr)" which can be used for any reference
    pressure value.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    t : array_like
        in situ temperature [:math:`^\circ` C (ITS-90)]
    p : array_like
        pressure [dbar]

    Returns
    -------
    pt0 : array_like
          potential temperature relative to 0 dbar [:math:`^\circ` C (ITS-90)]

    See Also
    --------
    entropy_part, gibbs_pt0_pt0, entropy_part_zerop

    Notes
    -----
    pt_from_t  has the same result (only slower)

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> t = [28.7856, 28.4329, 22.8103, 10.2600, 6.8863, 4.4036]
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> gsw.pt0_from_t(SA, t, p)
    array([ 28.78319682,  28.42098334,  22.7849304 ,  10.23052366,
             6.82923022,   4.32451057])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 3.1.

    .. [2] McDougall T. J., D. R. Jackett, P. M. Barker, C. Roberts-Thomson,
       R. Feistel and R. W. Hallberg, 2010:  A computationally efficient 25-term
       expression for the density of seawater in terms of Conservative
       Temperature, and related properties of seawater.
    """

    SA = np.maximum(SA, 0)

    s1 = SA * (35. / SSO)

    pt0 = t + p * (8.65483913395442e-6 -
             s1 * 1.41636299744881e-6 -
              p * 7.38286467135737e-9 +
              t * (-8.38241357039698e-6 +
             s1 * 2.83933368585534e-8 +
              t * 1.77803965218656e-8 +
              p * 1.71155619208233e-10))

    dentropy_dt = cp0 / ((Kelvin + pt0) * (1 - 0.05 * (1 - SA / SSO)))

    true_entropy_part = entropy_part(SA, t, p)

    for Number_of_iterations in range(0, 2, 1):
        pt0_old = pt0
        dentropy = entropy_part_zerop(SA, pt0_old) - true_entropy_part
        # Half way the mod. method (McDougall and Wotherspoon, 2012).
        pt0 = pt0_old - dentropy / dentropy_dt
        pt0m = 0.5 * (pt0 + pt0_old)
        dentropy_dt = -gibbs_pt0_pt0(SA, pt0m)
        pt0 = pt0_old - dentropy / dentropy_dt

    # maximum error of 6.3x10^-9 degrees C for one iteration. maximum error is
    # 1.8x10^-14 degrees C for two iterations (two iterations is the default,
    # "for Number_of_iterations = 1:2").  These errors are over the full
    # "oceanographic funnel" of McDougall et al. (2010), which reaches down to
    # p = 8000 dbar.

    return pt0


@match_args_return
def pt_from_CT(SA, CT):
    """
    Calculates potential temperature (with a reference sea pressure of zero
    dbar) from Conservative Temperature.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    CT : array_like
         Conservative Temperature [:math:`^\circ` C (ITS-90)]

    Returns
    -------
    pt : array_like
         potential temperature referenced to a sea pressure of zero dbar
         [:math:`^\circ` C (ITS-90)]

    See Also
    --------
    specvol_anom

    Notes
    -----
    This function uses 1.5 iterations through a modified Newton-Raphson (N-R)
    iterative solution procedure, starting from a rational-function-based
    initial condition for both pt and dCT_dpt.

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> CT = [28.8099, 28.4392, 22.7862, 10.2262, 6.8272, 4.3236]
    >>> gsw.pt_from_CT(SA, CT)
    array([ 28.78317705,  28.4209556 ,  22.78495347,  10.23053439,
             6.82921659,   4.32453484])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See sections 3.1 and 3.3.

    .. [2] McDougall T. J., D. R. Jackett, P. M. Barker, C. Roberts-Thomson,
       R. Feistel and R. W. Hallberg, 2010:  A computationally efficient
       25-term expression for the density of seawater in terms of Conservative
       Temperature, and related properties of seawater.
    """

    SA, CT, mask = strip_mask(SA, CT)
    SA = np.maximum(SA, 0)

    s1 = SA * 35. / SSO

    a0 = -1.446013646344788e-2
    a1 = -3.305308995852924e-3
    a2 = 1.062415929128982e-4
    a3 = 9.477566673794488e-1
    a4 = 2.166591947736613e-3
    a5 = 3.828842955039902e-3

    b0 = 1.000000000000000e+0
    b1 = 6.506097115635800e-4
    b2 = 3.830289486850898e-3
    b3 = 1.247811760368034e-6

    a5CT = a5 * CT
    b3CT = b3 * CT
    CT_factor = (a3 + a4 * s1 + a5CT)
    pt_num = a0 + s1 * (a1 + a2 * s1) + CT * CT_factor
    pt_den = b0 + b1 * s1 + CT * (b2 + b3CT)
    pt = pt_num / pt_den

    dCT_dpt = pt_den / (CT_factor + a5CT - (b2 + b3CT + b3CT) * pt)

    # 1.5 iterations through the modified Newton-Rapshon iterative method
    CT_diff = CT_from_pt(SA, pt) - CT
    pt_old = pt
    pt = pt_old - CT_diff / dCT_dpt  # 1/2-way through the 1st modified N-R.
    ptm = 0.5 * (pt + pt_old)

    # This routine calls gibbs_pt0_pt0(SA, pt0) to get the second derivative of
    # the Gibbs function with respect to temperature at zero sea pressure.

    dCT_dpt = -(ptm + Kelvin) * gibbs_pt0_pt0(SA, ptm) / cp0
    pt = pt_old - CT_diff / dCT_dpt  # End of 1st full modified N-R iteration.
    CT_diff = CT_from_pt(SA, pt) - CT
    pt_old = pt
    pt = pt_old - CT_diff / dCT_dpt  # 1.5 iterations of the modified N-R.
    # Abs max error of result is 1.42e-14 deg C.
    return np.ma.array(pt, mask=mask, copy=False)


@match_args_return
def pt_from_entropy(SA, entropy):
    """
    Calculates potential temperature with reference pressure p_ref = 0 dbar
    and with entropy as an input variable.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    entropy : array_like
              specific entropy [J kg :sup:`-1` K :sup:`-1`]

    Returns
    -------
    pt : array_like
         potential temperature [:math:`^\circ` C (ITS-90)]
         with reference sea pressure (p_ref) = 0 dbar.

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> entropy = [400.3892, 395.4378, 319.8668, 146.7910, 98.6471, 62.7919]
    >>> gsw.pt_from_entropy(SA, entropy)
    array([ 28.78317983,  28.42095483,  22.78495274,  10.23053207,
             6.82921333,   4.32453778])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See appendix  A.10.
    """

    SA = np.maximum(SA, 0)

    part1 = 1 - SA / SSO
    part2 = 1 - 0.05 * part1
    ent_SA = (cp0 / Kelvin) * part1 * (1 - 1.01 * part1)
    c = (entropy - ent_SA) * part2 / cp0
    pt = Kelvin * (np.exp(c) - 1)
    dentropy_dt = cp0 / ((Kelvin + pt) * part2)  # Initial dentropy_dt.

    for Number_of_iterations in range(0, 3):
        pt_old = pt
        dentropy = entropy_from_pt(SA, pt_old) - entropy
        # This is half way through the modified method
        # (McDougall and Wotherspoon, 2012)
        pt = pt_old - dentropy / dentropy_dt
        ptm = 0.5 * (pt + pt_old)
        dentropy_dt = -gibbs_pt0_pt0(SA, ptm)
        pt = pt_old - dentropy / dentropy_dt

    # maximum error of 2.2x10^-6 degrees C for one iteration. maximum error is
    # 1.4x10^-14 degrees C for two iterations (two iterations is the default,
    # "for Number_of_iterations = 1:2").

    return pt


@match_args_return
def pt_from_t(SA, t, p, p_ref=0):
    """
    Calculates potential temperature with the general reference pressure,
    pr, from in situ temperature.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    t : array_like
        in situ temperature [:math:`^\circ` C (ITS-90)]
    p : array_like
        pressure [dbar]
    p_ref : int, float, optional
         reference pressure, default = 0

    Returns
    -------
    pt : array_like
         potential temperature [:math:`^\circ` C (ITS-90)]

    Notes
    -----
    This function calls `entropy_part` which evaluates entropy except for the
    parts which are a function of Absolute Salinity alone. A faster routine
    exists pt0_from_t(SA,t,p) if p_ref is indeed zero dbar.

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> t = [28.7856, 28.4329, 22.8103, 10.2600, 6.8863, 4.4036]
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> gsw.pt_from_t(SA, t, p)
    array([ 28.78319682,  28.42098334,  22.7849304 ,  10.23052366,
             6.82923022,   4.32451057])
    >>> gsw.pt_from_t(SA, t, p, p_ref=1000)
    array([ 29.02665528,  28.662375  ,  22.99149634,  10.35341725,
             6.92732954,   4.4036    ])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See section 3.1.

    .. [2] McDougall T.J., P.M. Barker, R. Feistel and D.R. Jackett, 2011:  A
       computationally efficient 48-term expression for the density of seawater
       in terms of Conservative Temperature, and related properties of seawater.
    """

    p_ref = np.asanyarray(p_ref)

    SA = np.maximum(SA, 0)

    s1 = SA * 35. / SSO

    pt = (t + (p - p_ref) * (8.65483913395442e-6 -
                       s1 * 1.41636299744881e-6 -
              (p + p_ref) * 7.38286467135737e-9 +
                        t * (-8.38241357039698e-6 +
                       s1 * 2.83933368585534e-8 +
                        t * 1.77803965218656e-8 +
              (p + p_ref) * 1.71155619208233e-10)))

    dentropy_dt = cp0 / ((Kelvin + pt) *
                         (1 - 0.05 * (1 - SA / SSO)))

    true_entropy_part = entropy_part(SA, t, p)

    for Number_of_iterations in range(0, 2, 1):
        pt_old = pt
        dentropy = entropy_part(SA, pt_old, p_ref) - true_entropy_part
        pt = pt_old - dentropy / dentropy_dt  # half way through the method
        ptm = 0.5 * (pt + pt_old)
        dentropy_dt = -gibbs(n0, n2, n0, SA, ptm, p_ref)
        pt = pt_old - dentropy / dentropy_dt

    # maximum error of 6.3x10^-9 degrees C for one iteration.  maximum error
    # is 1.8x10^-14 degrees C for two iterations (two iterations is the default,
    # "for Number_of_iterations = 1:2).  These errors are over the full
    # "oceanographic funnel" of McDougall et al. (2010), which reaches down to
    # p = 8000 dbar.

    return pt


@match_args_return
def t90_from_t48(t48):
    """
    Converts IPTS-48 temperature to International Temperature Scale 1990
    (ITS-90) temperature.  This conversion should be applied to all in-situ
    data collected prior to 31/12/1967.

    Parameters
    ---------
    t48 : array_like
          in-situ temperature [:math:`^\circ` C (ITPS-48)]

    Returns
    -------
    t90 : array_like
          in-situ temperature [:math:`^\circ` C (ITS-90)]

    References
    ----------
    .. [1] International Temperature Scales of 1948, 1968 and 1990, an ICES
       note, available from http://www.ices.dk/ocean/procedures/its.htm
    """

    return (t48 - (4.4e-6) * t48 * (100 - t48)) / 1.00024


@match_args_return
def t90_from_t68(t68):
    """
    Converts IPTS-68 temperature to International Temperature Scale 1990
    (ITS-90) temperature.  This conversion should be applied to all in-situ
    data collected between 1/1/1968 and 31/12/1989.

    Parameters
    ---------
    t68 : array_like
          in-situ temperature [:math:`^\circ` C (ITPS-68)]

    Returns
    -------
    t90 : array_like
          in-situ temperature [:math:`^\circ` C (ITS-90)]

    References
    ----------
    .. [1] International Temperature Scales of 1948, 1968 and 1990, an ICES
       note, available from http://www.ices.dk/ocean/procedures/its.htm
    """

    return t68 * 0.999760057586179


@match_args_return
def t_from_CT(SA, CT, p):
    """
    Calculates *in-situ* temperature from Conservative Temperature of
    seawater.

    Parameters
    ----------
    SA : array_like
         Absolute salinity [g kg :sup:`-1`]
    CT : array_like
         Conservative Temperature [:math:`^\circ` C (ITS-90)]
    p : array_like
        pressure [dbar]

    Returns
    -------
    t : array_like
        in situ temperature [:math:`^\circ` C (ITS-90)]

    Examples
    --------
    >>> import gsw
    >>> SA = [34.7118, 34.8915, 35.0256, 34.8472, 34.7366, 34.7324]
    >>> CT = [28.8099, 28.4392, 22.7862, 10.2262, 6.8272, 4.3236]
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> gsw.t_from_CT(SA, CT, p)
    array([ 28.78558023,  28.43287225,  22.81032309,  10.26001075,
             6.8862863 ,   4.40362445])

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp. See sections 3.1 and 3.3.
    """

    pt0 = pt_from_CT(SA, CT)
    return pt_from_t(SA, pt0, 0, p)


@match_args_return
def t_from_entropy(SA, entropy, p):
    """
    gsw_t_from_entropy                                    in-situ temperature
                                                     as a function of entropy
    =========================================================================

    USAGE:
     t = gsw_t_from_entropy(SA,entropy,p)

    DESCRIPTION:
     Calculates in-situ temperature with entropy as an input variable.

    INPUT:
     SA       =  Absolute Salinity                                   [ g/kg ]
     entropy  =  specific entropy                                [ J/(kg*K) ]
     p   =  sea pressure                                             [ dbar ]
            ( i.e. absolute pressure - 10.1325 dbar )

     SA & entropy need to have the same dimensions.
     p may have dimensions 1x1 or Mx1 or 1xN or MxN, where SA & entropy are
     MxN.

    OUTPUT:
     t   =  in-situ temperature (ITS-90)                            [ deg C ]

    AUTHOR:
     Trevor McDougall and Paul Barker                    [ help@teos-10.org ]

    VERSION NUMBER: 3.03 (29th April, 2013)

    REFERENCES:
     IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of
      seawater - 2010: Calculation and use of thermodynamic properties.
      Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
      UNESCO (English), 196 pp.  Available from http://www.TEOS-10.org
      See appendix  A.10 of this TEOS-10 Manual.
    """

    pt = pt_from_entropy(SA, entropy)
    # Note that pt is potential temperature with a reference pressure of zero.
    p0 = 0
    return pt_from_t(SA, pt, p0, p)


def z_from_depth(depth):
    """
    Calculates height, z, from depth.  Note that in general height is
    negative in the ocean.

    Parameters
    ---------
    depth : array_like
        depth [m]

    Returns
    -------
    z : array_like
        height [m]
    """

    return -depth


@match_args_return
def z_from_p(p, lat, geo_strf_dyn_height=0):
    """
    Calculates height from sea pressure using the computationally-efficient
    48-term expression for density in terms of SA, CT and p (McDougall et
    al., 2011).  Dynamic height anomaly, geo_strf_dyn_height, if provided, must
    be computed with its pr=0 (the surface).

    Parameters
    ----------
    p : array_like
        pressure [dbar]
    lat : array_like
          latitude in decimal degrees north [-90..+90]
    geo_strf_dyn_height : float, optional
                          dynamic height anomaly [ m :sup:`2` s :sup:`-2` ]

    Returns
    -------
    z : array_like
        height [m]

    Examples
    --------
    >>> import gsw
    >>> p = [10, 50, 125, 250, 600, 1000]
    >>> lat = 4
    >>> gsw.z_from_p(p, lat)
    array([  -9.94458313,  -49.71808883, -124.27262301, -248.47007032,
           -595.82544461, -992.09217796])

    Notes
    -----
    At sea level z = 0, and since z (HEIGHT) is defined to be positive upwards,
    it follows that while z is positive in the atmosphere, it is NEGATIVE in
    the ocean.

    References
    ----------
    .. [1] IOC, SCOR and IAPSO, 2010: The international thermodynamic equation
       of seawater - 2010: Calculation and use of thermodynamic properties.
       Intergovernmental Oceanographic Commission, Manuals and Guides No. 56,
       UNESCO (English), 196 pp.

    .. [2] McDougall T.J., P.M. Barker, R. Feistel and D.R. Jackett, 2011:  A
       computationally efficient 48-term expression for the density of seawater
       in terms of Conservative Temperature, and related properties of seawater.

    .. [3] Moritz (2000) Goedetic reference system 1980. J. Geodesy, 74,
       128-133.
    """

    X = np.sin(lat * DEG2RAD)
    sin2 = X ** 2
    B = 9.780327 * (1.0 + (5.2792e-3 + (2.32e-5 * sin2)) * sin2)
    A = -0.5 * gamma * B
    C = enthalpy_SSO_0_p(p) - geo_strf_dyn_height

    return -2 * C / (B + np.sqrt(B ** 2 - 4 * A * C))


if __name__ == '__main__':
    import doctest
    doctest.testmod()
