from __future__ import division, print_function
# Make sasmodels available on the path
import sys, os
BETA_DIR = os.path.dirname(os.path.realpath(__file__))
SASMODELS_DIR = os.path.dirname(os.path.dirname(BETA_DIR))
sys.path.insert(0, SASMODELS_DIR)

from collections import namedtuple

from matplotlib import pyplot as plt
import numpy as np
from numpy import pi, sin, cos, sqrt, fabs
from numpy.polynomial.legendre import leggauss
from scipy.special import j1 as J1
from numpy import inf
from scipy.special import gammaln  # type: ignore

Theory = namedtuple('Theory', 'Q F1 F2 P S I Seff Ibeta')
Theory.__new__.__defaults__ = (None,) * len(Theory._fields)

#Used to calculate F(q) for the cylinder, sphere, ellipsoid models
def sas_sinx_x(x):
    with np.errstate(all='ignore'):
        retvalue = sin(x)/x
    retvalue[x == 0.] = 1.
    return retvalue

def sas_2J1x_x(x):
    with np.errstate(all='ignore'):
        retvalue = 2*J1(x)/x
    retvalue[x == 0] = 1.
    return retvalue

def sas_3j1x_x(x):
    """return 3*j1(x)/x"""
    retvalue = np.empty_like(x)
    with np.errstate(all='ignore'):
        # GSL bessel_j1 taylor expansion
        index = (x < 0.25)
        y = x[index]**2
        c1 = -1.0/10.0
        c2 = +1.0/280.0
        c3 = -1.0/15120.0
        c4 = +1.0/1330560.0
        c5 = -1.0/172972800.0
        retvalue[index] = 1.0 + y*(c1 + y*(c2 + y*(c3 + y*(c4 + y*c5))))
        index = ~index
        y = x[index]
        retvalue[index] = 3*(sin(y) - y*cos(y))/y**3
    retvalue[x == 0.] = 1.
    return retvalue

#Used to cross check my models with sasview models
def build_model(model_name, q, **pars):
    from sasmodels.core import load_model_info, build_model as build_sasmodel
    from sasmodels.data import empty_data1D
    from sasmodels.direct_model import DirectModel
    model_info = load_model_info(model_name)
    model = build_sasmodel(model_info, dtype='double!')
    data = empty_data1D(q)
    calculator = DirectModel(data, model,cutoff=0)
    calculator.pars = pars.copy()
    calculator.pars.setdefault('background', 0)
    return calculator

#gives the hardsphere structure factor that sasview uses
def _hardsphere_simple(q, radius_effective, volfraction):
    CUTOFFHS = 0.05
    if fabs(radius_effective) < 1.E-12:
        HARDSPH = 1.0
        return HARDSPH
    X = 1.0/(1.0 -volfraction)
    D = X*X
    A = (1.+2.*volfraction)*D
    A *= A
    X = fabs(q*radius_effective*2.0)
    if X < 5.E-06:
        HARDSPH = 1./A
        return HARDSPH
    X2 = X*X
    B = (1.0 +0.5*volfraction)*D
    B *= B
    B *= -6.*volfraction
    G = 0.5*volfraction*A
    if X < CUTOFFHS:
        FF = 8.0*A +6.0*B + 4.0*G + ( -0.8*A -B/1.5 -0.5*G +(A/35. +0.0125*B +0.02*G)*X2)*X2
        HARDSPH = 1./(1. + volfraction*FF )
        return HARDSPH
    X4 = X2*X2
    S, C = sin(X), cos(X)
    FF =  ((G*( (4.*X2 -24.)*X*S -(X4 -12.*X2 +24.)*C +24. )/X2 + B*(2.*X*S -(X2-2.)*C -2.) )/X + A*(S-X*C))/X
    HARDSPH = 1./(1. + 24.*volfraction*FF/X2)
    return HARDSPH

def hardsphere_simple(q, radius_effective, volfraction):
    SQ = [_hardsphere_simple(qk, radius_effective, volfraction) for qk in q]
    return np.array(SQ)

#Used in gaussian quadrature for polydispersity
#returns values and the probability of those values based on gaussian distribution
N_GAUSS = 35
NSIGMA_GAUSS = 3
def gaussian_distribution(center, sigma, lb, ub):
    #3 standard deviations covers approx. 99.7%
    if sigma != 0:
        nsigmas = NSIGMA_GAUSS
        x = np.linspace(center-sigma*nsigmas, center+sigma*nsigmas, num=N_GAUSS)
        x = x[(x >= lb) & (x <= ub)]
        px = np.exp((x-center)**2 / (-2.0 * sigma * sigma))
        return x, px
    else:
        return np.array([center]), np.array([1])

N_SCHULZ = 80
NSIGMA_SCHULZ = 8
def schulz_distribution(center, sigma, lb, ub):
    if sigma != 0:
        nsigmas = NSIGMA_SCHULZ
        x = np.linspace(center-sigma*nsigmas, center+sigma*nsigmas, num=N_SCHULZ)
        x = x[(x >= lb) & (x <= ub)]
        R = x/center
        z = (center/sigma)**2
        arg = z*np.log(z) + (z-1)*np.log(R) - R*z - np.log(center) - gammaln(z)
        px = np.exp(arg)
        return x, px
    else:
        return np.array([center]), np.array([1])

#returns the effective radius used in sasview
def ER_ellipsoid(radius_polar, radius_equatorial):
    ee = np.empty_like(radius_polar)
    if radius_polar > radius_equatorial:
        ee = (radius_polar**2 - radius_equatorial**2)/radius_polar**2
    elif radius_polar < radius_equatorial:
        ee = (radius_equatorial**2 - radius_polar**2) / radius_equatorial**2
    else:
        ee = 2*radius_polar
    if radius_polar * radius_equatorial != 0:
        bd = 1.0 - ee
        e1 = np.sqrt(ee)
        b1 = 1.0 + np.arcsin(e1) / (e1*np.sqrt(bd))
        bL = (1.0 + e1) / (1.0 - e1)
        b2 = 1.0 + bd / 2 / e1 * np.log(bL)
        delta = 0.75 * b1 * b2
    ddd = np.zeros_like(radius_polar)
    ddd = 2.0*(delta + 1.0)*radius_polar*radius_equatorial**2
    return 0.5*ddd**(1.0 / 3.0)

def ellipsoid_volume(radius_polar, radius_equatorial):
    volume = (4./3.)*pi*radius_polar*radius_equatorial**2
    return volume

# F1 is F(q)
# F2 is F(g)^2
#IQM is I(q) with monodispersity
#IQSM is I(q) with structure factor S(q) and monodispersity
#IQBM is I(q) with Beta Approximation and monodispersity
#SQ is monodisperse approach for structure factor
#SQ_EFF is the effective structure factor from beta approx
def ellipsoid_theta(q, radius_polar, radius_equatorial, sld, sld_solvent,
                    volfraction=0, radius_effective=None):
    #creates values z and corresponding probabilities w from legendre-gauss quadrature
    volume = ellipsoid_volume(radius_polar, radius_equatorial)
    z, w = leggauss(76)
    F1 = np.zeros_like(q)
    F2 = np.zeros_like(q)
    #use a u subsition(u=cos) and then u=(z+1)/2 to change integration from
    #0->2pi with respect to alpha to -1->1 with respect to z, allowing us to use
    #legendre-gauss quadrature
    for k, qk in enumerate(q):
        r = sqrt(radius_equatorial**2*(1-((z+1)/2)**2)+radius_polar**2*((z+1)/2)**2)
        form = (sld-sld_solvent)*volume*sas_3j1x_x(qk*r)
        F2[k] = np.sum(w*form**2)
        F1[k] = np.sum(w*form)
    #the 1/2 comes from the change of variables mentioned above
    F2 = F2/2.0
    F1 = F1/2.0
    if radius_effective is None:
        radius_effective = ER_ellipsoid(radius_polar,radius_equatorial)
    SQ = hardsphere_simple(q, radius_effective, volfraction)
    SQ_EFF = 1 + F1**2/F2*(SQ - 1)
    IQM = 1e-4*F2/volume
    IQSM = volfraction*IQM*SQ
    IQBM = volfraction*IQM*SQ_EFF
    return Theory(Q=q, F1=F1, F2=F2, P=IQM, S=SQ, I=IQSM, Seff=SQ_EFF, Ibeta=IQBM)

#IQD is I(q) polydispursed, IQSD is I(q)S(q) polydispursed, etc.
#IQBD HAS NOT BEEN CROSS CHECKED AT ALL
def ellipsoid_pe(q, radius_polar, radius_equatorial, sld, sld_solvent,
                 radius_polar_pd=0.1, radius_equatorial_pd=0.1,
                 radius_polar_pd_type='gaussian',
                 radius_equatorial_pd_type='gaussian',
                 volfraction=0, radius_effective=None,
                 background=0, scale=1,
                 norm='sasview'):
    if norm not in ['sasview', 'sasfit', 'yun']:
        raise TypeError("unknown norm "+norm)
    if radius_polar_pd_type == 'gaussian':
        Rp_val, Rp_prob = gaussian_distribution(radius_polar, radius_polar_pd*radius_polar, 0, inf)
    elif radius_polar_pd_type == 'schulz':
        Rp_val, Rp_prob = schulz_distribution(radius_polar, radius_polar_pd*radius_polar, 0, inf)
    if radius_equatorial_pd_type == 'gaussian':
        Re_val, Re_prob = gaussian_distribution(radius_equatorial, radius_equatorial_pd*radius_equatorial, 0, inf)
    elif radius_equatorial_pd_type == 'schulz':
        Re_val, Re_prob = schulz_distribution(radius_equatorial, radius_equatorial_pd*radius_equatorial, 0, inf)
    total_weight = total_volume = 0
    radius_eff = 0
    F1, F2 = np.zeros_like(q), np.zeros_like(q)
    for k, Rpk in enumerate(Rp_val):
        print("ellipsoid cycle", k, "of", len(Rp_val))
        for i, Rei in enumerate(Re_val):
            theory = ellipsoid_theta(q, Rpk, Rei, sld, sld_solvent)
            volume = ellipsoid_volume(Rpk, Rei)
            weight = Rp_prob[k]*Re_prob[i]
            total_weight += weight
            total_volume += weight*volume
            F1 += theory.F1*weight
            F2 += theory.F2*weight
            radius_eff += weight*ER_ellipsoid(Rpk, Rei)
    F1 /= total_weight
    F2 /= total_weight
    average_volume = total_volume/total_weight
    if radius_effective is None:
        radius_effective = radius_eff/total_weight
    if norm == 'sasfit':
        IQD = F2
    elif norm == 'sasview':
        # Note: internally, sasview uses F2/total_volume because:
        #   average_volume = total_volume/total_weight
        #   F2/total_weight / average_volume
        #     = F2/total_weight / total_volume/total_weight
        #     = F2/total_volume
        IQD = F2/average_volume*1e-4*volfraction
        F1 *= 1e-2  # Yun is using sld in 1/A^2, not 1e-6/A^2
        F2 *= 1e-4
    elif norm == 'yun':
        F1 *= 1e-6  # Yun is using sld in 1/A^2, not 1e-6/A^2
        F2 *= 1e-12
        IQD = F2/average_volume*1e8*volfraction
    SQ = hardsphere_simple(q, radius_effective, volfraction)
    beta = F1**2/F2
    SQ_EFF = 1 + beta*(SQ - 1)
    IQSD = IQD*SQ
    IQBD = IQD*SQ_EFF
    return Theory(Q=q, F1=F1, F2=F2, P=IQD, S=SQ, I=IQSD, Seff=SQ_EFF, Ibeta=IQBD)

#polydispersity for sphere
def sphere_r(q,radius,sld,sld_solvent,
             radius_pd=0.1, radius_pd_type='gaussian',
             volfraction=0, radius_effective=None,
             background=0, scale=1,
             norm='sasview'):
    if norm not in ['sasview', 'sasfit', 'yun']:
        raise TypeError("unknown norm "+norm)
    if radius_pd_type == 'gaussian':
        radius_val, radius_prob = gaussian_distribution(radius, radius_pd*radius, 0, inf)
    elif radius_pd_type == 'schulz':
        radius_val, radius_prob = schulz_distribution(radius, radius_pd*radius, 0, inf)
    total_weight = total_volume = 0
    F1 = np.zeros_like(q)
    F2 = np.zeros_like(q)
    for k, rk in enumerate(radius_val):
        volume = 4./3.*pi*rk**3
        total_weight += radius_prob[k]
        total_volume += radius_prob[k]*volume
        form = (sld-sld_solvent)*volume*sas_3j1x_x(q*rk)
        F2 += radius_prob[k]*form**2
        F1 += radius_prob[k]*form
    F1 /= total_weight
    F2 /= total_weight
    average_volume = total_volume/total_weight

    if radius_effective is None:
        radius_effective = radius
    average_volume = total_volume/total_weight
    if norm == 'sasfit':
        IQD = F2
    elif norm == 'sasview':
        IQD = F2/average_volume*1e-4*volfraction
    elif norm == 'yun':
        F1 *= 1e-6  # Yun is using sld in 1/A^2, not 1e-6/A^2
        F2 *= 1e-12
        IQD = F2/average_volume*1e8*volfraction
    SQ = hardsphere_simple(q, radius_effective, volfraction)
    beta = F1**2/F2
    SQ_EFF = 1 + beta*(SQ - 1)
    IQSD = IQD*SQ
    IQBD = IQD*SQ_EFF
    return Theory(Q=q, F1=F1, F2=F2, P=IQD, S=SQ, I=IQSD, Seff=SQ_EFF, Ibeta=IQBD)

###############################################################################
###############################################################################
###############################################################################
##################                                           ##################
##################                   TESTS                   ##################
##################                                           ##################
###############################################################################
###############################################################################
###############################################################################

def popn(d, keys):
    """
    Splits a dict into two, with any key of *d* which is in *keys* removed
    from *d* and added to *b*. Returns *b*.
    """
    b = {}
    for k in keys:
        try:
            b[k] = d.pop(k)
        except KeyError:
            pass
    return b

def sasmodels_theory(q, Pname, **pars):
    """
    Call sasmodels to compute the model with and without a hard sphere
    structure factor.
    """
    #mono_pars = {k: (0 if k.endswith('_pd') else v) for k, v in pars.items()}
    Ppars = pars.copy()
    Spars = popn(Ppars, ['radius_effective', 'volfraction'])
    Ipars = pars.copy()

    # Autofill npts and nsigmas for the given pd type
    for k, v in pars.items():
        if k.endswith("_pd_type"):
            if v == "gaussian":
                n, nsigmas = N_GAUSS, NSIGMA_GAUSS
            elif v == "schulz":
                n, nsigmas = N_SCHULZ, NSIGMA_SCHULZ
            Ppars.setdefault(k.replace("_pd_type", "_pd_n"), n)
            Ppars.setdefault(k.replace("_pd_type", "_pd_nsigma"), nsigmas)
            Ipars.setdefault(k.replace("_pd_type", "_pd_n"), n)
            Ipars.setdefault(k.replace("_pd_type", "_pd_nsigma"), nsigmas)

    #Ppars['scale'] = Spars.get('volfraction', 1)
    P = build_model(Pname, q)
    S = build_model("hardsphere", q)
    I = build_model(Pname+"@hardsphere", q)
    Pq = P(**Ppars)*pars.get('volfraction', 1)
    Sq = S(**Spars)
    Iq = I(**Ipars)
    #Iq = Pq*Sq*pars.get('volfraction', 1)
    #Sq = Iq/Pq
    #Iq = None#= Sq = None
    r = dict(I._kernel.results())
    return Theory(Q=q, F1=None, F2=None, P=Pq, S=None, I=None, Seff=r["S_eff(Q)"], Ibeta=Iq)

def compare(title, target, actual, fields='F1 F2 P S I Seff Ibeta'):
    """
    Plot fields in common between target and actual, along with relative error.
    """
    available = [s for s in fields.split()
                 if getattr(target, s) is not None and getattr(actual, s) is not None]
    rows = len(available)
    for row, field in enumerate(available):
        Q = target.Q
        I1, I2 = getattr(target, field), getattr(actual, field)
        plt.subplot(rows, 2, 2*row+1)
        plt.loglog(Q, abs(I1), label="target "+field)
        plt.loglog(Q, abs(I2), label="value "+field)
        #plt.legend(loc="upper left", bbox_to_anchor=(1,1))
        plt.legend(loc='lower left')
        plt.subplot(rows, 2, 2*row+2)
        plt.semilogx(Q, I2/I1 - 1, label="relative error")
        #plt.semilogx(Q, I1/I2 - 1, label="relative error")
    plt.tight_layout()
    plt.suptitle(title)
    plt.show()

def data_file(name):
    return os.path.join(BETA_DIR, 'data_files', name)

def load_sasfit(path):
    data = np.loadtxt(path, dtype=str, delimiter=';').T
    data = np.vstack((map(float, v) for v in data[0:2]))
    return data

COMPARISON = {}  # Type: Dict[(str,str,str)] -> Callable[(), None]

def compare_sasview_sphere(pd_type='schulz'):
    q = np.logspace(-5, 0, 250)
    model = 'sphere'
    pars = dict(
        radius=20, sld=4, sld_solvent=1,
        background=0,
        radius_pd=.1, radius_pd_type=pd_type,
        volfraction=0.15,
        #radius_effective=12.59921049894873,  # equivalent average sphere radius
        )
    target = sasmodels_theory(q, model, **pars)
    actual = sphere_r(q, norm='sasview', **pars)
    title = " ".join(("sasmodels", model, pd_type))
    compare(title, target, actual)
COMPARISON[('sasview', 'sphere', 'gaussian')] = lambda: compare_sasview_sphere(pd_type='gaussian')
COMPARISON[('sasview', 'sphere', 'schulz')] = lambda: compare_sasview_sphere(pd_type='schulz')

def compare_sasview_ellipsoid(pd_type='gaussian'):
    q = np.logspace(-5, 0, 50)
    model = 'ellipsoid'
    pars = dict(
        radius_polar=20, radius_equatorial=400, sld=4, sld_solvent=1,
        background=0,
        radius_polar_pd=0.1, radius_polar_pd_type=pd_type,
        radius_equatorial_pd=0.1, radius_equatorial_pd_type=pd_type,
        volfraction=0.15,
        radius_effective=270.7543927018,
        #radius_effective=12.59921049894873,
        )
    target = sasmodels_theory(q, model, radius_effective_mode=0, structure_factor_mode=1, **pars)
    actual = ellipsoid_pe(q, norm='sasview', **pars)
    title = " ".join(("sasmodels", model, pd_type))
    compare(title, target, actual)
COMPARISON[('sasview', 'ellipsoid', 'gaussian')] = lambda: compare_sasview_ellipsoid(pd_type='gaussian')
COMPARISON[('sasview', 'ellipsoid', 'schulz')] = lambda: compare_sasview_ellipsoid(pd_type='schulz')

def compare_yun_ellipsoid_mono():
    pars = {
        'radius_polar': 20, 'radius_polar_pd': 0, 'radius_polar_pd_type': 'gaussian',
        'radius_equatorial': 10, 'radius_equatorial_pd': 0, 'radius_equatorial_pd_type': 'gaussian',
        'sld': 2, 'sld_solvent': 1,
        'volfraction': 0.15,
        # Yun uses radius for same volume sphere for effective radius
        # whereas sasview uses the average curvature.
        'radius_effective': 12.59921049894873,
    }

    data = np.loadtxt(data_file('yun_ellipsoid.dat'),skiprows=2).T
    Q = data[0]
    F1 = data[1]
    P = data[3]*pars['volfraction']
    S = data[5]
    Seff = data[6]
    target = Theory(Q=Q, F1=F1, P=P, S=S, Seff=Seff)
    actual = ellipsoid_pe(Q, norm='yun', **pars)
    title = " ".join(("yun", "ellipsoid", "no pd"))
    #compare(title, target, actual, fields="P S I Seff Ibeta")
    compare(title, target, actual)
COMPARISON[('yun', 'ellipsoid', 'gaussian')] = compare_yun_ellipsoid_mono
COMPARISON[('yun', 'ellipsoid', 'schulz')] = compare_yun_ellipsoid_mono

def compare_yun_sphere_gauss():
    # Note: yun uses gauss limits from R0/10 to R0 + 5 sigma steps sigma/100
    # With pd = 0.1, that's 14 sigma and 1400 points.
    pars = {
        'radius': 20, 'radius_pd': 0.1, 'radius_pd_type': 'gaussian',
        'sld': 6, 'sld_solvent': 0,
        'volfraction': 0.1,
    }

    data = np.loadtxt(data_file('testPolydisperseGaussianSphere.dat'), skiprows=2).T
    Q = data[0]
    F1 = data[1]
    F2 = data[2]
    P = data[3]
    S = data[5]
    Seff = data[6]
    target = Theory(Q=Q, F1=F1, P=P, S=S, Seff=Seff)
    actual = sphere_r(Q, norm='yun', **pars)
    title = " ".join(("yun", "sphere", "10% dispersion 10% Vf"))
    compare(title, target, actual)
    data = np.loadtxt(data_file('testPolydisperseGaussianSphere2.dat'), skiprows=2).T
    pars.update(radius_pd=0.15)
    Q = data[0]
    F1 = data[1]
    F2 = data[2]
    P = data[3]
    S = data[5]
    Seff = data[6]
    target = Theory(Q=Q, F1=F1, P=P, S=S, Seff=Seff)
    actual = sphere_r(Q, norm='yun', **pars)
    title = " ".join(("yun", "sphere", "15% dispersion 10% Vf"))
    compare(title, target, actual)
COMPARISON[('yun', 'sphere', 'gaussian')] = compare_yun_sphere_gauss


def compare_sasfit_sphere_gauss():
    #N=1,s=2,X0=20,distr radius R=20,eta_core=4,eta_solv=1,.3
    pars = {
        'radius': 20, 'radius_pd': 0.1, 'radius_pd_type': 'gaussian',
        'sld': 4, 'sld_solvent': 1,
        'volfraction': 0.3,
    }

    Q, IQ = load_sasfit(data_file('sasfit_sphere_IQD.txt'))
    Q, IQSD = load_sasfit(data_file('sasfit_sphere_IQSD.txt'))
    Q, IQBD = load_sasfit(data_file('sasfit_sphere_IQBD.txt'))
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_sphere_sq.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_sphere_sqeff.txt'))
    target = Theory(Q=Q, F1=None, F2=None, P=IQ, S=SQ, I=IQSD, Seff=SQ_EFF, Ibeta=IQBD)
    actual = sphere_r(Q, norm="sasfit", **pars)
    title = " ".join(("sasfit", "sphere", "pd=10% gaussian"))
    compare(title, target, actual)
    #compare(title, target, actual, fields="P")
COMPARISON[('sasfit', 'sphere', 'gaussian')] = compare_sasfit_sphere_gauss

def compare_sasfit_sphere_schulz():
    #radius=20,sld=4,sld_solvent=1,volfraction=0.3,radius_pd=0.1
    #We have scaled the output from sasfit by 1e-4*volume*volfraction
    #0.10050378152592121
    pars = {
        'radius': 20, 'radius_pd': 0.1, 'radius_pd_type': 'schulz',
        'sld': 4, 'sld_solvent': 1,
        'volfraction': 0.3,
    }

    Q, IQ = load_sasfit(data_file('sasfit_sphere_schulz_IQD.txt'))
    Q, IQSD = load_sasfit(data_file('sasfit_sphere_schulz_IQSD.txt'))
    Q, IQBD = load_sasfit(data_file('sasfit_sphere_schulz_IQBD.txt'))
    target = Theory(Q=Q, F1=None, F2=None, P=IQ, S=None, I=IQSD, Seff=None, Ibeta=IQBD)
    actual = sphere_r(Q, norm="sasfit", **pars)
    title = " ".join(("sasfit", "sphere", "pd=10% schulz"))
    compare(title, target, actual)
COMPARISON[('sasfit', 'sphere', 'schulz')] = compare_sasfit_sphere_schulz

def compare_sasfit_ellipsoid_schulz():
    #polarradius=20, equatorialradius=10, sld=4,sld_solvent=1,volfraction=0.3,radius_polar_pd=0.1
    #Effective radius =13.1353356684
    #We have scaled the output from sasfit by 1e-4*volume*volfraction
    #0.10050378152592121
    pars = {
        'radius_polar': 20, 'radius_polar_pd': 0.1, 'radius_polar_pd_type': 'schulz',
        'radius_equatorial': 10, 'radius_equatorial_pd': 0., 'radius_equatorial_pd_type': 'schulz',
        'sld': 4, 'sld_solvent': 1,
        'volfraction': 0.3, 'radius_effective': 13.1353356684,
    }

    Q, IQ = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQD.txt'))
    Q, IQSD = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQSD.txt'))
    Q, IQBD = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQBD.txt'))
    target = Theory(Q=Q, F1=None, F2=None, P=IQ, S=None, I=IQSD, Seff=None, Ibeta=IQBD)
    actual = ellipsoid_pe(Q, norm="sasfit", **pars)
    title = " ".join(("sasfit", "ellipsoid", "pd=10% schulz"))
    compare(title, target, actual)
COMPARISON[('sasfit', 'ellipsoid', 'schulz')] = compare_sasfit_ellipsoid_schulz


def compare_sasfit_ellipsoid_gaussian():
    pars = {
        'radius_polar': 20, 'radius_polar_pd': 0, 'radius_polar_pd_type': 'gaussian',
        'radius_equatorial': 10, 'radius_equatorial_pd': 0, 'radius_equatorial_pd_type': 'gaussian',
        'sld': 4, 'sld_solvent': 1,
        'volfraction': 0, 'radius_effective': None,
    }

    #Rp=20,Re=10,eta_core=4,eta_solv=1
    Q, PQ0 = load_sasfit(data_file('sasfit_ellipsoid_IQM.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.0, radius_equatorial_pd=0, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ0)
    compare("sasfit ellipsoid no poly", target, actual); plt.show()

    #N=1,s=2,X0=20,distr 10% polar Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Rp10 = load_sasfit(data_file('sasfit_ellipsoid_IQD.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.1, radius_equatorial_pd=0.0, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Rp10)
    compare("sasfit ellipsoid P(Q) 10% Rp", target, actual); plt.show()
    #N=1,s=1,X0=10,distr 10% equatorial Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Re10 = load_sasfit(data_file('sasfit_ellipsoid_IQD2.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.0, radius_equatorial_pd=0.1, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Re10)
    compare("sasfit ellipsoid P(Q) 10% Re", target, actual); plt.show()
    #N=1,s=6,X0=20,distr 30% polar Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Rp30 = load_sasfit(data_file('sasfit_ellipsoid_IQD3.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.3, radius_equatorial_pd=0.0, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Rp30)
    compare("sasfit ellipsoid P(Q) 30% Rp", target, actual); plt.show()
    #N=1,s=3,X0=10,distr 30% equatorial Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Re30 = load_sasfit(data_file('sasfit_ellipsoid_IQD4.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.0, radius_equatorial_pd=0.3, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Re30)
    compare("sasfit ellipsoid P(Q) 30% Re", target, actual); plt.show()
    #N=1,s=12,X0=20,distr 60% polar Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Rp60 = load_sasfit(data_file('sasfit_ellipsoid_IQD5.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.6, radius_equatorial_pd=0.0, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Rp60)
    compare("sasfit ellipsoid P(Q) 60% Rp", target, actual); plt.show()
    #N=1,s=6,X0=10,distr 60% equatorial Rp=20,Re=10,eta_core=4,eta_solv=1, no structure poly
    Q, PQ_Re60 = load_sasfit(data_file('sasfit_ellipsoid_IQD6.txt'))
    pars.update(volfraction=0, radius_polar_pd=0.0, radius_equatorial_pd=0.6, radius_effective=None)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, P=PQ_Re60)
    compare("sasfit ellipsoid P(Q) 60% Re", target, actual); plt.show()

    #N=1,s=2,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.1354236254,.15
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff.txt'))
    pars.update(volfraction=0.15, radius_polar_pd=0.1, radius_equatorial_pd=0, radius_effective=13.1354236254)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 10% Rp 15% Vf", target, actual); plt.show()
    #N=1,s=6,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.0901197149,.15
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq2.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff2.txt'))
    pars.update(volfraction=0.15, radius_polar_pd=0.3, radius_equatorial_pd=0, radius_effective=13.0901197149)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 30% Rp 15% Vf", target, actual); plt.show()
    #N=1,s=12,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.336060917,.15
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq3.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff3.txt'))
    pars.update(volfraction=0.15, radius_polar_pd=0.6, radius_equatorial_pd=0, radius_effective=13.336060917)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 60% Rp 15% Vf", target, actual); plt.show()

    #N=1,s=2,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.1354236254,.3
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq4.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff4.txt'))
    pars.update(volfraction=0.3, radius_polar_pd=0.1, radius_equatorial_pd=0, radius_effective=13.1354236254)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 10% Rp 30% Vf", target, actual); plt.show()
    #N=1,s=6,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.0901197149,.3
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq5.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff5.txt'))
    pars.update(volfraction=0.3, radius_polar_pd=0.3, radius_equatorial_pd=0, radius_effective=13.0901197149)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 30% Rp 30% Vf", target, actual); plt.show()
    #N=1,s=12,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.336060917,.3
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq6.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff6.txt'))
    pars.update(volfraction=0.3, radius_polar_pd=0.6, radius_equatorial_pd=0, radius_effective=13.336060917)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 60% Rp 30% Vf", target, actual); plt.show()

    #N=1,s=2,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.1354236254,.6
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq7.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff7.txt'))
    pars.update(volfraction=0.6, radius_polar_pd=0.1, radius_equatorial_pd=0, radius_effective=13.1354236254)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 10% Rp 60% Vf", target, actual); plt.show()
    #N=1,s=6,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.0901197149,.6
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq8.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff8.txt'))
    pars.update(volfraction=0.6, radius_polar_pd=0.3, radius_equatorial_pd=0, radius_effective=13.0901197149)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 30% Rp 60% Vf", target, actual); plt.show()
    #N=1,s=12,X0=20,distr polar Rp=20,Re=10,eta_core=4,eta_solv=1, hardsphere ,13.336060917,.6
    Q, SQ = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sq9.txt'))
    Q, SQ_EFF = load_sasfit(data_file('sasfit_polydisperse_ellipsoid_sqeff9.txt'))
    pars.update(volfraction=0.6, radius_polar_pd=0.6, radius_equatorial_pd=0, radius_effective=13.336060917)
    actual = ellipsoid_pe(Q, norm='sasfit', **pars)
    target = Theory(Q=Q, S=SQ, Seff=SQ_EFF)
    compare("sasfit ellipsoid P(Q) 60% Rp 60% Vf", target, actual); plt.show()
COMPARISON[('sasfit', 'ellipsoid', 'gaussian')] = compare_sasfit_ellipsoid_gaussian

def main():
    key = tuple(sys.argv[1:])
    if key not in COMPARISON:
        print("usage: sasfit_compare.py [sasview|sasfit|yun] [sphere|ellipsoid] [gaussian|schulz]")
        return
    comparison = COMPARISON[key]
    comparison()

if __name__ == "__main__":
    main()
