use core::fmt;
use std::array::IntoIter;

use crate::{
    calc_result::CalcResult,
    expressions::{parser::Node, token::Error, types::CellReferenceIndex},
    model::Model,
};

pub(crate) mod binary_search;
mod database;
mod date_and_time;
mod engineering;
mod financial;
mod financial_util;
mod information;
mod logical;
mod lookup_and_reference;
mod macros;
mod math_util;
mod mathematical;
mod mathematical_sum;
mod statistical;
mod subtotal;
mod text;
mod text_util;
pub(crate) mod util;
mod xlookup;

/// List of all implemented functions
#[derive(PartialEq, Clone, Debug)]
pub enum Function {
    // Logical
    And,
    False,
    If,
    Iferror,
    Ifna,
    Ifs,
    Not,
    Or,
    Switch,
    True,
    Xor,

    // Mathematical and trigonometry
    Abs,
    Acos,
    Acosh,
    Asin,
    Asinh,
    Atan,
    Atan2,
    Atanh,
    Choose,
    Column,
    Columns,
    Cos,
    Cosh,
    Log,
    Log10,
    Ln,
    Max,
    Min,
    Pi,
    Power,
    Product,
    Rand,
    Randbetween,
    Round,
    Rounddown,
    Roundup,
    Sin,
    Sinh,
    Sqrt,
    Sqrtpi,
    Sum,
    Sumif,
    Sumifs,
    Sumx2my2,
    Sumx2py2,
    Sumxmy2,
    Tan,
    Tanh,
    Acot,
    Acoth,
    Cot,
    Coth,
    Csc,
    Csch,
    Sec,
    Sech,
    Exp,
    Fact,
    Factdouble,
    Sign,
    Radians,
    Degrees,
    Int,
    Even,
    Odd,
    Ceiling,
    CeilingMath,
    CeilingPrecise,
    Floor,
    FloorMath,
    FloorPrecise,
    IsoCeiling,
    Mod,
    Quotient,
    Mround,
    Trunc,
    Gcd,
    Lcm,
    Base,
    Decimal,
    Roman,
    Arabic,
    Combin,
    Combina,
    Sumsq,

    // Information
    ErrorType,
    Formulatext,
    Isblank,
    Iserr,
    Iserror,
    Iseven,
    Isformula,
    Islogical,
    Isna,
    Isnontext,
    Isnumber,
    Isodd,
    Isref,
    Istext,
    Na,
    Sheet,
    Type,

    Sheets,
    N,
    Cell,
    Info,

    // Lookup and reference
    Hlookup,
    Index,
    Indirect,
    Lookup,
    Match,
    Offset,
    Row,
    Rows,
    Vlookup,
    Xlookup,

    // Text
    Concat,
    Concatenate,
    Exact,
    Find,
    Left,
    Len,
    Lower,
    Mid,
    Rept,
    Right,
    Search,
    Substitute,
    T,
    Text,
    Textafter,
    Textbefore,
    Textjoin,
    Trim,
    Unicode,
    Upper,
    Value,
    Valuetotext,

    // Statistical
    Average,
    Averagea,
    Averageif,
    Averageifs,
    Count,
    Counta,
    Countblank,
    Countif,
    Countifs,
    Maxifs,
    Minifs,
    Geomean,

    Avedev,
    BetaDist,
    BetaInv,
    BinomDist,
    BinomDistRange,
    BinomInv,
    ChisqDist,
    ChisqDistRT,
    ChisqInv,
    ChisqInvRT,
    ChisqTest,
    ConfidenceNorm,
    ConfidenceT,
    CovarianceP,
    CovarianceS,
    Devsq,
    ExponDist,
    FDist,
    FDistRT,
    FInv,
    FInvRT,
    FTest,
    Fisher,
    FisherInv,
    // Forecast,
    Gamma,
    GammaDist,
    GammaInv,
    GammaLn,
    GammaLnPrecise,
    Gauss,
    Harmean,
    HypGeomDist,
    Kurt,
    Large,
    // Linest,
    // Logest,
    LogNormDist,
    LogNormInv,
    MaxA,
    Median,
    MinA,
    // ModeMult,
    // ModeSingl,
    NegbinomDist,
    NormDist,
    NormInv,
    NormSdist,
    NormSInv,
    Pearson,
    // PercentileExc,
    // PercentileInc,
    // PercentrankExc,
    // PercentrankInc,
    // Permut,
    // Permutationa,
    Phi,
    PoissonDist,
    // Prob,
    // QuartileExc,
    // QuartileInc,
    RankAvg,
    RankEq,
    Skew,
    SkewP,
    Small,
    Standardize,
    StDevP,
    StDevS,
    Stdeva,
    Stdevpa,
    TDist,
    TDist2T,
    TDistRT,
    TInv,
    TInv2T,
    TTest,
    // Trend,
    // Trimmean,
    VarP,
    VarS,
    VarpA,
    VarA,
    WeibullDist,
    ZTest,

    // Date and time
    Date,
    Datedif,
    Datevalue,
    Day,
    Edate,
    Eomonth,
    Month,
    Time,
    Timevalue,
    Hour,
    Minute,
    Second,
    Now,
    Today,
    Year,
    Networkdays,
    NetworkdaysIntl,
    Days,
    Days360,
    Weekday,
    Weeknum,
    Workday,
    WorkdayIntl,
    Yearfrac,
    Isoweeknum,

    // Financial
    Cumipmt,
    Cumprinc,
    Db,
    Ddb,
    Dollarde,
    Dollarfr,
    Effect,
    Fv,
    Ipmt,
    Irr,
    Ispmt,
    Mirr,
    Nominal,
    Nper,
    Npv,
    Pduration,
    Pmt,
    Ppmt,
    Pv,
    Rate,
    Rri,
    Sln,
    Syd,
    Tbilleq,
    Tbillprice,
    Tbillyield,
    Xirr,
    Xnpv,

    // Engineering: Bessel and transcendental functions
    Besseli,
    Besselj,
    Besselk,
    Bessely,
    Erf,
    Erfc,
    ErfcPrecise,
    ErfPrecise,

    // Engineering: Number systems
    Bin2dec,
    Bin2hex,
    Bin2oct,
    Dec2Bin,
    Dec2hex,
    Dec2oct,
    Hex2bin,
    Hex2dec,
    Hex2oct,
    Oct2bin,
    Oct2dec,
    Oct2hex,

    // Engineering: Bit functions
    Bitand,
    Bitlshift,
    Bitor,
    Bitrshift,
    Bitxor,

    // Engineering: Complex functions
    Complex,
    Imabs,
    Imaginary,
    Imargument,
    Imconjugate,
    Imcos,
    Imcosh,
    Imcot,
    Imcsc,
    Imcsch,
    Imdiv,
    Imexp,
    Imln,
    Imlog10,
    Imlog2,
    Impower,
    Improduct,
    Imreal,
    Imsec,
    Imsech,
    Imsin,
    Imsinh,
    Imsqrt,
    Imsub,
    Imsum,
    Imtan,

    // Engineering: Misc function
    Convert,
    Delta,
    Gestep,
    Subtotal,

    // Database
    Daverage,
    Dcount,
    Dget,
    Dmax,
    Dmin,
    Dsum,
    Dcounta,
    Dproduct,
    Dstdev,
    Dvar,
    Dvarp,
    Dstdevp,

    Correl,
    Rsq,
    Intercept,
    Slope,
    Steyx,
}

impl Function {
    pub fn into_iter() -> IntoIter<Function, 345> {
        [
            Function::And,
            Function::False,
            Function::If,
            Function::Iferror,
            Function::Ifna,
            Function::Ifs,
            Function::Not,
            Function::Or,
            Function::Switch,
            Function::True,
            Function::Xor,
            Function::Sin,
            Function::Cos,
            Function::Tan,
            Function::Asin,
            Function::Acos,
            Function::Atan,
            Function::Sinh,
            Function::Cosh,
            Function::Tanh,
            Function::Asinh,
            Function::Acosh,
            Function::Atanh,
            Function::Abs,
            Function::Pi,
            Function::Ln,
            Function::Log,
            Function::Log10,
            Function::Sqrt,
            Function::Sqrtpi,
            Function::Atan2,
            Function::Acot,
            Function::Acoth,
            Function::Cot,
            Function::Coth,
            Function::Csc,
            Function::Csch,
            Function::Sec,
            Function::Sech,
            Function::Power,
            Function::Exp,
            Function::Fact,
            Function::Factdouble,
            Function::Sign,
            Function::Int,
            Function::Even,
            Function::Odd,
            Function::Ceiling,
            Function::CeilingMath,
            Function::CeilingPrecise,
            Function::Floor,
            Function::FloorMath,
            Function::FloorPrecise,
            Function::IsoCeiling,
            Function::Mod,
            Function::Quotient,
            Function::Mround,
            Function::Trunc,
            Function::Gcd,
            Function::Lcm,
            Function::Base,
            Function::Decimal,
            Function::Max,
            Function::Min,
            Function::Product,
            Function::Rand,
            Function::Randbetween,
            Function::Radians,
            Function::Degrees,
            Function::Round,
            Function::Rounddown,
            Function::Roundup,
            Function::Sum,
            Function::Sumif,
            Function::Sumifs,
            Function::Sumx2my2,
            Function::Sumx2py2,
            Function::Sumxmy2,
            Function::Choose,
            Function::Column,
            Function::Columns,
            Function::Index,
            Function::Indirect,
            Function::Hlookup,
            Function::Lookup,
            Function::Match,
            Function::Offset,
            Function::Row,
            Function::Rows,
            Function::Vlookup,
            Function::Xlookup,
            Function::Concatenate,
            Function::Exact,
            Function::Value,
            Function::T,
            Function::Valuetotext,
            Function::Concat,
            Function::Find,
            Function::Left,
            Function::Len,
            Function::Lower,
            Function::Mid,
            Function::Right,
            Function::Search,
            Function::Text,
            Function::Trim,
            Function::Unicode,
            Function::Upper,
            Function::Isnumber,
            Function::Isnontext,
            Function::Istext,
            Function::Islogical,
            Function::Isblank,
            Function::Iserr,
            Function::Iserror,
            Function::Isna,
            Function::Na,
            Function::Isref,
            Function::Isodd,
            Function::Iseven,
            Function::ErrorType,
            Function::Formulatext,
            Function::Isformula,
            Function::Type,
            Function::Sheet,
            Function::Average,
            Function::Avedev,
            Function::Averagea,
            Function::Averageif,
            Function::Averageifs,
            Function::Count,
            Function::Counta,
            Function::Countblank,
            Function::Countif,
            Function::Countifs,
            Function::Maxifs,
            Function::Minifs,
            Function::Geomean,
            Function::Year,
            Function::Day,
            Function::Month,
            Function::Eomonth,
            Function::Date,
            Function::Datedif,
            Function::Datevalue,
            Function::Edate,
            Function::Networkdays,
            Function::NetworkdaysIntl,
            Function::Time,
            Function::Timevalue,
            Function::Hour,
            Function::Minute,
            Function::Second,
            Function::Today,
            Function::Now,
            Function::Days,
            Function::Days360,
            Function::Weekday,
            Function::Weeknum,
            Function::Workday,
            Function::WorkdayIntl,
            Function::Yearfrac,
            Function::Isoweeknum,
            Function::Pmt,
            Function::Pv,
            Function::Rate,
            Function::Nper,
            Function::Fv,
            Function::Ppmt,
            Function::Ipmt,
            Function::Npv,
            Function::Mirr,
            Function::Irr,
            Function::Xirr,
            Function::Xnpv,
            Function::Rept,
            Function::Textafter,
            Function::Textbefore,
            Function::Textjoin,
            Function::Substitute,
            Function::Ispmt,
            Function::Rri,
            Function::Sln,
            Function::Syd,
            Function::Nominal,
            Function::Effect,
            Function::Pduration,
            Function::Tbillyield,
            Function::Tbillprice,
            Function::Tbilleq,
            Function::Dollarde,
            Function::Dollarfr,
            Function::Ddb,
            Function::Db,
            Function::Cumprinc,
            Function::Cumipmt,
            Function::Besseli,
            Function::Besselj,
            Function::Besselk,
            Function::Bessely,
            Function::Erf,
            Function::ErfPrecise,
            Function::Erfc,
            Function::ErfcPrecise,
            Function::Bin2dec,
            Function::Bin2hex,
            Function::Bin2oct,
            Function::Dec2Bin,
            Function::Dec2hex,
            Function::Dec2oct,
            Function::Hex2bin,
            Function::Hex2dec,
            Function::Hex2oct,
            Function::Oct2bin,
            Function::Oct2dec,
            Function::Oct2hex,
            Function::Bitand,
            Function::Bitlshift,
            Function::Bitor,
            Function::Bitrshift,
            Function::Bitxor,
            Function::Complex,
            Function::Imabs,
            Function::Imaginary,
            Function::Imargument,
            Function::Imconjugate,
            Function::Imcos,
            Function::Imcosh,
            Function::Imcot,
            Function::Imcsc,
            Function::Imcsch,
            Function::Imdiv,
            Function::Imexp,
            Function::Imln,
            Function::Imlog10,
            Function::Imlog2,
            Function::Impower,
            Function::Improduct,
            Function::Imreal,
            Function::Imsec,
            Function::Imsech,
            Function::Imsin,
            Function::Imsinh,
            Function::Imsqrt,
            Function::Imsub,
            Function::Imsum,
            Function::Imtan,
            Function::Convert,
            Function::Delta,
            Function::Gestep,
            Function::Subtotal,
            Function::Roman,
            Function::Arabic,
            Function::Combin,
            Function::Combina,
            Function::Sumsq,
            Function::N,
            Function::Cell,
            Function::Info,
            Function::Sheets,
            Function::Daverage,
            Function::Dcount,
            Function::Dget,
            Function::Dmax,
            Function::Dmin,
            Function::Dsum,
            Function::Dcounta,
            Function::Dproduct,
            Function::Dstdev,
            Function::Dvar,
            Function::Dvarp,
            Function::Dstdevp,
            Function::BetaDist,
            Function::BetaInv,
            Function::BinomDist,
            Function::BinomDistRange,
            Function::BinomInv,
            Function::ChisqDist,
            Function::ChisqDistRT,
            Function::ChisqInv,
            Function::ChisqInvRT,
            Function::ChisqTest,
            Function::ConfidenceNorm,
            Function::ConfidenceT,
            Function::CovarianceP,
            Function::CovarianceS,
            Function::Devsq,
            Function::ExponDist,
            Function::FDist,
            Function::FDistRT,
            Function::FInv,
            Function::FInvRT,
            Function::FTest,
            Function::Fisher,
            Function::FisherInv,
            Function::Gamma,
            Function::GammaDist,
            Function::GammaInv,
            Function::GammaLn,
            Function::GammaLnPrecise,
            Function::HypGeomDist,
            Function::LogNormDist,
            Function::LogNormInv,
            Function::NegbinomDist,
            Function::NormDist,
            Function::NormInv,
            Function::NormSdist,
            Function::NormSInv,
            Function::Pearson,
            Function::Phi,
            Function::PoissonDist,
            Function::Standardize,
            Function::StDevP,
            Function::StDevS,
            Function::Stdeva,
            Function::Stdevpa,
            Function::TDist,
            Function::TDist2T,
            Function::TDistRT,
            Function::TInv,
            Function::TInv2T,
            Function::TTest,
            Function::VarP,
            Function::VarS,
            Function::VarpA,
            Function::VarA,
            Function::WeibullDist,
            Function::ZTest,
            Function::Correl,
            Function::Rsq,
            Function::Intercept,
            Function::Slope,
            Function::Steyx,
            Function::Large,
            Function::Median,
            Function::Small,
            Function::RankAvg,
            Function::RankEq,
            Function::Skew,
            Function::SkewP,
            Function::Harmean,
            Function::Gauss,
            Function::Kurt,
            Function::MaxA,
            Function::MinA,
        ]
        .into_iter()
    }
}

impl Function {
    /// Some functions in Excel like CONCAT are stringified as `_xlfn.CONCAT`.
    pub fn to_xlsx_string(&self) -> String {
        match self {
            Function::Concat => "_xlfn.CONCAT".to_string(),
            Function::Ifna => "_xlfn.IFNA".to_string(),
            Function::Ifs => "_xlfn.IFS".to_string(),
            Function::Maxifs => "_xlfn.MAXIFS".to_string(),
            Function::Minifs => "_xlfn.MINIFS".to_string(),
            Function::Switch => "_xlfn.SWITCH".to_string(),
            Function::Xlookup => "_xlfn.XLOOKUP".to_string(),
            Function::Xor => "_xlfn.XOR".to_string(),
            Function::Textbefore => "_xlfn.TEXTBEFORE".to_string(),
            Function::Textafter => "_xlfn.TEXTAFTER".to_string(),
            Function::Textjoin => "_xlfn.TEXTJOIN".to_string(),
            Function::Unicode => "_xlfn.UNICODE".to_string(),
            Function::Rri => "_xlfn.RRI".to_string(),
            Function::Pduration => "_xlfn.PDURATION".to_string(),
            Function::Bitand => "_xlfn.BITAND".to_string(),
            Function::Bitor => "_xlfn.BITOR".to_string(),
            Function::Bitxor => "_xlfn.BITXOR".to_string(),
            Function::Bitlshift => "_xlfn.BITLSHIFT".to_string(),
            Function::Bitrshift => "_xlfn.BITRSHIFT".to_string(),
            Function::Imtan => "_xlfn.IMTAN".to_string(),
            Function::Imsinh => "_xlfn.IMSINH".to_string(),
            Function::Imcosh => "_xlfn.IMCOSH".to_string(),
            Function::Imcot => "_xlfn.IMCOT".to_string(),
            Function::Imcsc => "_xlfn.IMCSC".to_string(),
            Function::Imcsch => "_xlfn.IMCSCH".to_string(),
            Function::Imsec => "_xlfn.IMSEC".to_string(),
            Function::ErfcPrecise => "_xlfn.ERFC.PRECISE".to_string(),
            Function::ErfPrecise => "_xlfn.ERF.PRECISE".to_string(),
            Function::Valuetotext => "_xlfn.VALUETOTEXT".to_string(),
            Function::Isformula => "_xlfn.ISFORMULA".to_string(),
            Function::Sheet => "_xlfn.SHEET".to_string(),
            Function::Formulatext => "_xlfn.FORMULATEXT".to_string(),
            Function::Isoweeknum => "_xlfn.ISOWEEKNUM".to_string(),
            Function::Ceiling => "_xlfn.CEILING".to_string(),
            Function::CeilingMath => "_xlfn.CEILING.MATH".to_string(),
            Function::CeilingPrecise => "_xlfn.CEILING.PRECISE".to_string(),
            Function::FloorMath => "_xlfn.FLOOR.MATH".to_string(),
            Function::FloorPrecise => "_xlfn.FLOOR.PRECISE".to_string(),
            Function::IsoCeiling => "_xlfn.ISO.CEILING".to_string(),
            Function::Base => "_xlfn.BASE".to_string(),
            Function::Decimal => "_xlfn.DECIMAL".to_string(),
            Function::Arabic => "_xlfn.ARABIC".to_string(),
            Function::Combina => "_xlfn.COMBINA".to_string(),
            Function::Sheets => "_xlfn.SHEETS".to_string(),
            Function::Acoth => "_xlfn.ACOTH".to_string(),
            Function::Cot => "_xlfn.COT".to_string(),
            Function::Coth => "_xlfn.COTH".to_string(),
            Function::Csc => "_xlfn.CSC".to_string(),
            Function::Csch => "_xlfn.CSCH".to_string(),
            Function::Sec => "_xlfn.SEC".to_string(),
            Function::Sech => "_xlfn.SECH".to_string(),
            Function::Acot => "_xlfn.ACOT".to_string(),
            Function::GammaLnPrecise => "_xlfn.GAMMALN.PRECISE".to_string(),
            Function::Gamma => "_xlfn.GAMMA".to_string(),
            Function::GammaInv => "_xlfn.GAMMA.INV".to_string(),
            Function::GammaLn => "_xlfn.GAMMALN".to_string(),
            Function::BetaDist => "_xlfn.BETA.DIST".to_string(),
            Function::BetaInv => "_xlfn.BETA.INV".to_string(),

            Function::BinomDist => "_xlfn.BINOM.DIST".to_string(),
            Function::BinomDistRange => "_xlfn.BINOM.DIST.RANGE".to_string(),
            Function::BinomInv => "_xlfn.BINOM.INV".to_string(),
            Function::NegbinomDist => "_xlfn.NEGBINOM.DIST".to_string(),

            Function::ChisqDist => "_xlfn.CHISQ.DIST".to_string(),
            Function::ChisqDistRT => "_xlfn.CHISQ.DIST.RT".to_string(),
            Function::ChisqInv => "_xlfn.CHISQ.INV".to_string(),
            Function::ChisqInvRT => "_xlfn.CHISQ.INV.RT".to_string(),
            Function::ChisqTest => "_xlfn.CHISQ.TEST".to_string(),

            Function::ConfidenceNorm => "_xlfn.CONFIDENCE.NORM".to_string(),
            Function::ConfidenceT => "_xlfn.CONFIDENCE.T".to_string(),

            Function::CovarianceP => "_xlfn.COVARIANCE.P".to_string(),
            Function::CovarianceS => "_xlfn.COVARIANCE.S".to_string(),

            Function::ExponDist => "_xlfn.EXPON.DIST".to_string(),

            Function::FDist => "_xlfn.F.DIST".to_string(),
            Function::FDistRT => "_xlfn.F.DIST.RT".to_string(),
            Function::FInv => "_xlfn.F.INV".to_string(),
            Function::FInvRT => "_xlfn.F.INV.RT".to_string(),
            Function::FTest => "_xlfn.F.TEST".to_string(),

            Function::HypGeomDist => "_xlfn.HYPGEOM.DIST".to_string(),

            Function::LogNormDist => "_xlfn.LOGNORM.DIST".to_string(),
            Function::LogNormInv => "_xlfn.LOGNORM.INV".to_string(),

            Function::NormDist => "_xlfn.NORM.DIST".to_string(),
            Function::NormInv => "_xlfn.NORM.INV".to_string(),
            Function::NormSdist => "_xlfn.NORM.S.DIST".to_string(),
            Function::NormSInv => "_xlfn.NORM.S.INV".to_string(),

            Function::Phi => "_xlfn.PHI".to_string(),

            Function::PoissonDist => "_xlfn.POISSON.DIST".to_string(),

            Function::StDevP => "_xlfn.STDEV.P".to_string(),
            Function::StDevS => "_xlfn.STDEV.S".to_string(),

            Function::TDist => "_xlfn.T.DIST".to_string(),
            Function::TDist2T => "_xlfn.T.DIST.2T".to_string(),
            Function::TDistRT => "_xlfn.T.DIST.RT".to_string(),
            Function::TInv => "_xlfn.T.INV".to_string(),
            Function::TInv2T => "_xlfn.T.INV.2T".to_string(),
            Function::TTest => "_xlfn.T.TEST".to_string(),

            Function::VarP => "_xlfn.VAR.P".to_string(),
            Function::VarS => "_xlfn.VAR.S".to_string(),

            Function::WeibullDist => "_xlfn.WEIBULL.DIST".to_string(),
            Function::ZTest => "_xlfn.Z.TEST".to_string(),
            Function::SkewP => "_xlfn.SKEW.P".to_string(),
            Function::RankAvg => "_xlfn.RANK.AVG".to_string(),
            Function::RankEq => "_xlfn.RANK.EQ".to_string(),

            _ => self.to_string(),
        }
    }

    pub(crate) fn returns_reference(&self) -> bool {
        matches!(self, Function::Indirect | Function::Offset)
    }
    /// Gets the function from the name.
    /// Note that in Excel some (modern) functions are prefixed by `_xlfn.`
    pub fn get_function(name: &str) -> Option<Function> {
        match name.to_ascii_uppercase().as_str() {
            "AND" => Some(Function::And),
            "FALSE" => Some(Function::False),
            "IF" => Some(Function::If),
            "IFERROR" => Some(Function::Iferror),
            "IFNA" | "_XLFN.IFNA" => Some(Function::Ifna),
            "IFS" | "_XLFN.IFS" => Some(Function::Ifs),
            "NOT" => Some(Function::Not),
            "OR" => Some(Function::Or),
            "SWITCH" | "_XLFN.SWITCH" => Some(Function::Switch),
            "TRUE" => Some(Function::True),
            "XOR" | "_XLFN.XOR" => Some(Function::Xor),
            "SIN" => Some(Function::Sin),
            "COS" => Some(Function::Cos),
            "TAN" => Some(Function::Tan),
            "ASIN" => Some(Function::Asin),
            "ACOS" => Some(Function::Acos),
            "ATAN" => Some(Function::Atan),
            "SINH" => Some(Function::Sinh),
            "COSH" => Some(Function::Cosh),
            "TANH" => Some(Function::Tanh),
            "ASINH" => Some(Function::Asinh),
            "ACOSH" => Some(Function::Acosh),
            "ATANH" => Some(Function::Atanh),
            "ACOT" | "_XLFN.ACOT" => Some(Function::Acot),
            "COTH" | "_XLFN.COTH" => Some(Function::Coth),
            "COT" | "_XLFN.COT" => Some(Function::Cot),
            "CSC" | "_XLFN.CSC" => Some(Function::Csc),
            "CSCH" | "_XLFN.CSCH" => Some(Function::Csch),
            "SEC" | "_XLFN.SEC" => Some(Function::Sec),
            "SECH" | "_XLFN.SECH" => Some(Function::Sech),
            "ACOTH" | "_XLFN.ACOTH" => Some(Function::Acoth),
            "FACT" => Some(Function::Fact),
            "FACTDOUBLE" => Some(Function::Factdouble),
            "EXP" => Some(Function::Exp),
            "SIGN" => Some(Function::Sign),
            "RADIANS" => Some(Function::Radians),
            "DEGREES" => Some(Function::Degrees),
            "INT" => Some(Function::Int),
            "EVEN" => Some(Function::Even),
            "ODD" => Some(Function::Odd),
            "CEILING" | "_XLFN.CEILING" => Some(Function::Ceiling),
            "CEILING.MATH" | "_XLFN.CEILING.MATH" => Some(Function::CeilingMath),
            "CEILING.PRECISE" | "_XLFN.CEILING.PRECISE" => Some(Function::CeilingPrecise),
            "FLOOR" => Some(Function::Floor),
            "FLOOR.MATH" | "_XLFN.FLOOR.MATH" => Some(Function::FloorMath),
            "FLOOR.PRECISE" | "_XLFN.FLOOR.PRECISE" => Some(Function::FloorPrecise),
            "ISO.CEILING" | "_XLFN.ISO.CEILING" => Some(Function::IsoCeiling),
            "MOD" => Some(Function::Mod),
            "QUOTIENT" => Some(Function::Quotient),
            "MROUND" => Some(Function::Mround),
            "TRUNC" => Some(Function::Trunc),
            "GCD" => Some(Function::Gcd),
            "LCM" => Some(Function::Lcm),
            "BASE" | "_XLFN.BASE" => Some(Function::Base),
            "DECIMAL" | "_XLFN.DECIMAL" => Some(Function::Decimal),
            "ROMAN" => Some(Function::Roman),
            "ARABIC" | "_XLFN.ARABIC" => Some(Function::Arabic),
            "PI" => Some(Function::Pi),
            "ABS" => Some(Function::Abs),
            "SQRT" => Some(Function::Sqrt),
            "SQRTPI" => Some(Function::Sqrtpi),
            "POWER" => Some(Function::Power),
            "ATAN2" => Some(Function::Atan2),
            "LN" => Some(Function::Ln),
            "LOG" => Some(Function::Log),
            "LOG10" => Some(Function::Log10),
            "MAX" => Some(Function::Max),
            "MIN" => Some(Function::Min),
            "PRODUCT" => Some(Function::Product),
            "RAND" => Some(Function::Rand),
            "RANDBETWEEN" => Some(Function::Randbetween),
            "ROUND" => Some(Function::Round),
            "ROUNDDOWN" => Some(Function::Rounddown),
            "ROUNDUP" => Some(Function::Roundup),
            "SUM" => Some(Function::Sum),
            "SUMIF" => Some(Function::Sumif),
            "SUMIFS" => Some(Function::Sumifs),
            "COMBIN" => Some(Function::Combin),
            "COMBINA" | "_XLFN.COMBINA" => Some(Function::Combina),
            "SUMSQ" => Some(Function::Sumsq),

            // Lookup and Reference
            "CHOOSE" => Some(Function::Choose),
            "COLUMN" => Some(Function::Column),
            "COLUMNS" => Some(Function::Columns),
            "INDEX" => Some(Function::Index),
            "INDIRECT" => Some(Function::Indirect),
            "HLOOKUP" => Some(Function::Hlookup),
            "LOOKUP" => Some(Function::Lookup),
            "MATCH" => Some(Function::Match),
            "OFFSET" => Some(Function::Offset),
            "ROW" => Some(Function::Row),
            "ROWS" => Some(Function::Rows),
            "VLOOKUP" => Some(Function::Vlookup),
            "XLOOKUP" | "_XLFN.XLOOKUP" => Some(Function::Xlookup),

            "CONCATENATE" => Some(Function::Concatenate),
            "EXACT" => Some(Function::Exact),
            "VALUE" => Some(Function::Value),
            "T" => Some(Function::T),
            "VALUETOTEXT" | "_XLFN.VALUETOTEXT" => Some(Function::Valuetotext),
            "CONCAT" | "_XLFN.CONCAT" => Some(Function::Concat),
            "FIND" => Some(Function::Find),
            "LEFT" => Some(Function::Left),
            "LEN" => Some(Function::Len),
            "LOWER" => Some(Function::Lower),
            "MID" => Some(Function::Mid),
            "RIGHT" => Some(Function::Right),
            "SEARCH" => Some(Function::Search),
            "TEXT" => Some(Function::Text),
            "TRIM" => Some(Function::Trim),
            "UNICODE" | "_XLFN.UNICODE" => Some(Function::Unicode),
            "UPPER" => Some(Function::Upper),

            "REPT" => Some(Function::Rept),
            "TEXTAFTER" | "_XLFN.TEXTAFTER" => Some(Function::Textafter),
            "TEXTBEFORE" | "_XLFN.TEXTBEFORE" => Some(Function::Textbefore),
            "TEXTJOIN" | "_XLFN.TEXTJOIN" => Some(Function::Textjoin),
            "SUBSTITUTE" => Some(Function::Substitute),

            "ISNUMBER" => Some(Function::Isnumber),
            "ISNONTEXT" => Some(Function::Isnontext),
            "ISTEXT" => Some(Function::Istext),
            "ISLOGICAL" => Some(Function::Islogical),
            "ISBLANK" => Some(Function::Isblank),
            "ISERR" => Some(Function::Iserr),
            "ISERROR" => Some(Function::Iserror),
            "ISNA" => Some(Function::Isna),
            "NA" => Some(Function::Na),
            "ISREF" => Some(Function::Isref),
            "ISODD" => Some(Function::Isodd),
            "ISEVEN" => Some(Function::Iseven),
            "ERROR.TYPE" => Some(Function::ErrorType),
            "FORMULATEXT" | "_XLFN.FORMULATEXT" => Some(Function::Formulatext),
            "ISFORMULA" | "_XLFN.ISFORMULA" => Some(Function::Isformula),
            "TYPE" => Some(Function::Type),
            "SHEET" | "_XLFN.SHEET" => Some(Function::Sheet),

            "AVERAGE" => Some(Function::Average),
            "AVERAGEA" => Some(Function::Averagea),
            "AVEDEV" => Some(Function::Avedev),
            "AVERAGEIF" => Some(Function::Averageif),
            "AVERAGEIFS" => Some(Function::Averageifs),
            "COUNT" => Some(Function::Count),
            "COUNTA" => Some(Function::Counta),
            "COUNTBLANK" => Some(Function::Countblank),
            "COUNTIF" => Some(Function::Countif),
            "COUNTIFS" => Some(Function::Countifs),
            "MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs),
            "MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs),
            "GEOMEAN" => Some(Function::Geomean),
            // Date and Time
            "YEAR" => Some(Function::Year),
            "DAY" => Some(Function::Day),
            "EOMONTH" => Some(Function::Eomonth),
            "MONTH" => Some(Function::Month),
            "DATE" => Some(Function::Date),
            "DATEDIF" => Some(Function::Datedif),
            "DATEVALUE" => Some(Function::Datevalue),
            "EDATE" => Some(Function::Edate),
            "NETWORKDAYS" => Some(Function::Networkdays),
            "NETWORKDAYS.INTL" => Some(Function::NetworkdaysIntl),
            "TIME" => Some(Function::Time),
            "TIMEVALUE" => Some(Function::Timevalue),
            "HOUR" => Some(Function::Hour),
            "MINUTE" => Some(Function::Minute),
            "SECOND" => Some(Function::Second),
            "TODAY" => Some(Function::Today),
            "NOW" => Some(Function::Now),
            "DAYS" | "_XLFN.DAYS" => Some(Function::Days),
            "DAYS360" => Some(Function::Days360),
            "WEEKDAY" => Some(Function::Weekday),
            "WEEKNUM" => Some(Function::Weeknum),
            "WORKDAY" => Some(Function::Workday),
            "WORKDAY.INTL" => Some(Function::WorkdayIntl),
            "YEARFRAC" => Some(Function::Yearfrac),
            "ISOWEEKNUM" | "_XLFN.ISOWEEKNUM" => Some(Function::Isoweeknum),
            // Financial
            "PMT" => Some(Function::Pmt),
            "PV" => Some(Function::Pv),
            "RATE" => Some(Function::Rate),
            "NPER" => Some(Function::Nper),
            "FV" => Some(Function::Fv),
            "PPMT" => Some(Function::Ppmt),
            "IPMT" => Some(Function::Ipmt),
            "NPV" => Some(Function::Npv),
            "XNPV" => Some(Function::Xnpv),
            "MIRR" => Some(Function::Mirr),
            "IRR" => Some(Function::Irr),
            "XIRR" => Some(Function::Xirr),
            "ISPMT" => Some(Function::Ispmt),
            "RRI" | "_XLFN.RRI" => Some(Function::Rri),

            "SLN" => Some(Function::Sln),
            "SYD" => Some(Function::Syd),
            "NOMINAL" => Some(Function::Nominal),
            "EFFECT" => Some(Function::Effect),
            "PDURATION" | "_XLFN.PDURATION" => Some(Function::Pduration),

            "TBILLYIELD" => Some(Function::Tbillyield),
            "TBILLPRICE" => Some(Function::Tbillprice),
            "TBILLEQ" => Some(Function::Tbilleq),

            "DOLLARDE" => Some(Function::Dollarde),
            "DOLLARFR" => Some(Function::Dollarfr),

            "DDB" => Some(Function::Ddb),
            "DB" => Some(Function::Db),

            "CUMPRINC" => Some(Function::Cumprinc),
            "CUMIPMT" => Some(Function::Cumipmt),

            "BESSELI" => Some(Function::Besseli),
            "BESSELJ" => Some(Function::Besselj),
            "BESSELK" => Some(Function::Besselk),
            "BESSELY" => Some(Function::Bessely),
            "ERF" => Some(Function::Erf),
            "ERF.PRECISE" | "_XLFN.ERF.PRECISE" => Some(Function::ErfPrecise),
            "ERFC" => Some(Function::Erfc),
            "ERFC.PRECISE" | "_XLFN.ERFC.PRECISE" => Some(Function::ErfcPrecise),
            "BIN2DEC" => Some(Function::Bin2dec),
            "BIN2HEX" => Some(Function::Bin2hex),
            "BIN2OCT" => Some(Function::Bin2oct),
            "DEC2BIN" => Some(Function::Dec2Bin),
            "DEC2HEX" => Some(Function::Dec2hex),
            "DEC2OCT" => Some(Function::Dec2oct),
            "HEX2BIN" => Some(Function::Hex2bin),
            "HEX2DEC" => Some(Function::Hex2dec),
            "HEX2OCT" => Some(Function::Hex2oct),
            "OCT2BIN" => Some(Function::Oct2bin),
            "OCT2DEC" => Some(Function::Oct2dec),
            "OCT2HEX" => Some(Function::Oct2hex),
            "BITAND" | "_XLFN.BITAND" => Some(Function::Bitand),
            "BITLSHIFT" | "_XLFN.BITLSHIFT" => Some(Function::Bitlshift),
            "BITOR" | "_XLFN.BITOR" => Some(Function::Bitor),
            "BITRSHIFT" | "_XLFN.BITRSHIFT" => Some(Function::Bitrshift),
            "BITXOR" | "_XLFN.BITXOR" => Some(Function::Bitxor),
            "COMPLEX" => Some(Function::Complex),
            "IMABS" => Some(Function::Imabs),
            "IMAGINARY" => Some(Function::Imaginary),
            "IMARGUMENT" => Some(Function::Imargument),
            "IMCONJUGATE" => Some(Function::Imconjugate),
            "IMCOS" => Some(Function::Imcos),
            "IMCOSH" | "_XLFN.IMCOSH" => Some(Function::Imcosh),
            "IMCOT" | "_XLFN.IMCOT" => Some(Function::Imcot),
            "IMCSC" | "_XLFN.IMCSC" => Some(Function::Imcsc),
            "IMCSCH" | "_XLFN.IMCSCH" => Some(Function::Imcsch),
            "IMDIV" => Some(Function::Imdiv),
            "IMEXP" => Some(Function::Imexp),
            "IMLN" => Some(Function::Imln),
            "IMLOG10" => Some(Function::Imlog10),
            "IMLOG2" => Some(Function::Imlog2),
            "IMPOWER" => Some(Function::Impower),
            "IMPRODUCT" => Some(Function::Improduct),
            "IMREAL" => Some(Function::Imreal),
            "IMSEC" | "_XLFN.IMSEC" => Some(Function::Imsec),
            "IMSECH" | "_XLFN.IMSECH" => Some(Function::Imsech),
            "IMSIN" => Some(Function::Imsin),
            "IMSINH" | "_XLFN.IMSINH" => Some(Function::Imsinh),
            "IMSQRT" => Some(Function::Imsqrt),
            "IMSUB" => Some(Function::Imsub),
            "IMSUM" => Some(Function::Imsum),
            "IMTAN" | "_XLFN.IMTAN" => Some(Function::Imtan),
            "CONVERT" => Some(Function::Convert),
            "DELTA" => Some(Function::Delta),
            "GESTEP" => Some(Function::Gestep),

            "SUBTOTAL" => Some(Function::Subtotal),

            "N" => Some(Function::N),
            "CELL" => Some(Function::Cell),
            "INFO" => Some(Function::Info),
            "SHEETS" | "_XLFN.SHEETS" => Some(Function::Sheets),

            "DAVERAGE" => Some(Function::Daverage),
            "DCOUNT" => Some(Function::Dcount),
            "DGET" => Some(Function::Dget),
            "DMAX" => Some(Function::Dmax),
            "DMIN" => Some(Function::Dmin),
            "DSUM" => Some(Function::Dsum),
            "DCOUNTA" => Some(Function::Dcounta),
            "DPRODUCT" => Some(Function::Dproduct),
            "DSTDEV" => Some(Function::Dstdev),
            "DVAR" => Some(Function::Dvar),
            "DVARP" => Some(Function::Dvarp),
            "DSTDEVP" => Some(Function::Dstdevp),

            "BETA.DIST" | "_XLFN.BETA.DIST" => Some(Function::BetaDist),
            "BETA.INV" | "_XLFN.BETA.INV" => Some(Function::BetaInv),
            "BINOM.DIST" | "_XLFN.BINOM.DIST" => Some(Function::BinomDist),
            "BINOM.DIST.RANGE" | "_XLFN.BINOM.DIST.RANGE" => Some(Function::BinomDistRange),
            "BINOM.INV" | "_XLFN.BINOM.INV" => Some(Function::BinomInv),
            "CHISQ.DIST" | "_XLFN.CHISQ.DIST" => Some(Function::ChisqDist),
            "CHISQ.DIST.RT" | "_XLFN.CHISQ.DIST.RT" => Some(Function::ChisqDistRT),
            "CHISQ.INV" | "_XLFN.CHISQ.INV" => Some(Function::ChisqInv),
            "CHISQ.INV.RT" | "_XLFN.CHISQ.INV.RT" => Some(Function::ChisqInvRT),
            "CHISQ.TEST" | "_XLFN.CHISQ.TEST" => Some(Function::ChisqTest),
            "CONFIDENCE.NORM" | "_XLFN.CONFIDENCE.NORM" => Some(Function::ConfidenceNorm),
            "CONFIDENCE.T" | "_XLFN.CONFIDENCE.T" => Some(Function::ConfidenceT),
            "COVARIANCE.P" | "_XLFN.COVARIANCE.P" => Some(Function::CovarianceP),
            "COVARIANCE.S" | "_XLFN.COVARIANCE.S" => Some(Function::CovarianceS),
            "DEVSQ" => Some(Function::Devsq),
            "EXPON.DIST" | "_XLFN.EXPON.DIST" => Some(Function::ExponDist),
            "F.DIST" | "_XLFN.F.DIST" => Some(Function::FDist),
            "F.DIST.RT" | "_XLFN.F.DIST.RT" => Some(Function::FDistRT),
            "F.INV" | "_XLFN.F.INV" => Some(Function::FInv),
            "F.INV.RT" | "_XLFN.F.INV.RT" => Some(Function::FInvRT),
            "F.TEST" | "_XLFN.F.TEST" => Some(Function::FTest),
            "FISHER" => Some(Function::Fisher),
            "FISHERINV" => Some(Function::FisherInv),
            "GAMMA" | "_XLFN.GAMMA" => Some(Function::Gamma),
            "GAMMA.DIST" | "_XLFN.GAMMA.DIST" => Some(Function::GammaDist),
            "GAMMA.INV" | "_XLFN.GAMMA.INV" => Some(Function::GammaInv),
            "GAMMALN" | "_XLFN.GAMMALN" => Some(Function::GammaLn),
            "GAMMALN.PRECISE" | "_XLFN.GAMMALN.PRECISE" => Some(Function::GammaLnPrecise),
            "HYPGEOM.DIST" | "_XLFN.HYPGEOM.DIST" => Some(Function::HypGeomDist),
            "LOGNORM.DIST" | "_XLFN.LOGNORM.DIST" => Some(Function::LogNormDist),
            "LOGNORM.INV" | "_XLFN.LOGNORM.INV" => Some(Function::LogNormInv),
            "NEGBINOM.DIST" | "_XLFN.NEGBINOM.DIST" => Some(Function::NegbinomDist),
            "NORM.DIST" | "_XLFN.NORM.DIST" => Some(Function::NormDist),
            "NORM.INV" | "_XLFN.NORM.INV" => Some(Function::NormInv),
            "NORM.S.DIST" | "_XLFN.NORM.S.DIST" => Some(Function::NormSdist),
            "NORM.S.INV" | "_XLFN.NORM.S.INV" => Some(Function::NormSInv),
            "PEARSON" => Some(Function::Pearson),
            "PHI" | "_XLFN.PHI" => Some(Function::Phi),
            "POISSON.DIST" | "_XLFN.POISSON.DIST" => Some(Function::PoissonDist),
            "STANDARDIZE" => Some(Function::Standardize),
            "STDEV.P" | "_XLFN.STDEV.P" => Some(Function::StDevP),
            "STDEV.S" | "_XLFN.STDEV.S" => Some(Function::StDevS),
            "STDEVA" => Some(Function::Stdeva),
            "STDEVPA" => Some(Function::Stdevpa),
            "T.DIST" | "_XLFN.T.DIST" => Some(Function::TDist),
            "T.DIST.2T" | "_XLFN.T.DIST.2T" => Some(Function::TDist2T),
            "T.DIST.RT" | "_XLFN.T.DIST.RT" => Some(Function::TDistRT),
            "T.INV" | "_XLFN.T.INV" => Some(Function::TInv),
            "T.INV.2T" | "_XLFN.T.INV.2T" => Some(Function::TInv2T),
            "T.TEST" | "_XLFN.T.TEST" => Some(Function::TTest),
            "VAR.P" | "_XLFN.VAR.P" => Some(Function::VarP),
            "VAR.S" | "_XLFN.VAR.S" => Some(Function::VarS),
            "VARPA" => Some(Function::VarpA),
            "VARA" => Some(Function::VarA),
            "WEIBULL.DIST" | "_XLFN.WEIBULL.DIST" => Some(Function::WeibullDist),
            "Z.TEST" | "_XLFN.Z.TEST" => Some(Function::ZTest),
            "SUMX2MY2" => Some(Function::Sumx2my2),
            "SUMX2PY2" => Some(Function::Sumx2py2),
            "SUMXMY2" => Some(Function::Sumxmy2),
            "CORREL" => Some(Function::Correl),
            "RSQ" => Some(Function::Rsq),
            "INTERCEPT" => Some(Function::Intercept),
            "SLOPE" => Some(Function::Slope),
            "STEYX" => Some(Function::Steyx),

            "SKEW.P" | "_XLFN.SKEW.P" => Some(Function::SkewP),
            "SKEW" => Some(Function::Skew),
            "KURT" => Some(Function::Kurt),
            "HARMEAN" => Some(Function::Harmean),
            "MEDIAN" => Some(Function::Median),
            "GAUSS" => Some(Function::Gauss),

            "MINA" => Some(Function::MinA),
            "MAXA" => Some(Function::MaxA),
            "SMALL" => Some(Function::Small),
            "LARGE" => Some(Function::Large),
            "RANK.EQ" | "_XLFN.RANK.EQ" => Some(Function::RankEq),
            "RANK.AVG" | "_XLFN.RANK.AVG" => Some(Function::RankAvg),

            _ => None,
        }
    }
}

impl fmt::Display for Function {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Function::And => write!(f, "AND"),
            Function::False => write!(f, "FALSE"),
            Function::If => write!(f, "IF"),
            Function::Iferror => write!(f, "IFERROR"),
            Function::Ifna => write!(f, "IFNA"),
            Function::Ifs => write!(f, "IFS"),
            Function::Not => write!(f, "NOT"),
            Function::Or => write!(f, "OR"),
            Function::Switch => write!(f, "SWITCH"),
            Function::True => write!(f, "TRUE"),
            Function::Xor => write!(f, "XOR"),
            Function::Log => write!(f, "LOG"),
            Function::Log10 => write!(f, "LOG10"),
            Function::Ln => write!(f, "LN"),
            Function::Sin => write!(f, "SIN"),
            Function::Cos => write!(f, "COS"),
            Function::Tan => write!(f, "TAN"),
            Function::Asin => write!(f, "ASIN"),
            Function::Acos => write!(f, "ACOS"),
            Function::Atan => write!(f, "ATAN"),
            Function::Sinh => write!(f, "SINH"),
            Function::Cosh => write!(f, "COSH"),
            Function::Tanh => write!(f, "TANH"),
            Function::Asinh => write!(f, "ASINH"),
            Function::Acosh => write!(f, "ACOSH"),
            Function::Atanh => write!(f, "ATANH"),
            Function::Acot => write!(f, "ACOT"),
            Function::Acoth => write!(f, "ACOTH"),
            Function::Cot => write!(f, "COT"),
            Function::Coth => write!(f, "COTH"),
            Function::Csc => write!(f, "CSC"),
            Function::Csch => write!(f, "CSCH"),
            Function::Sec => write!(f, "SEC"),
            Function::Sech => write!(f, "SECH"),
            Function::Abs => write!(f, "ABS"),
            Function::Pi => write!(f, "PI"),
            Function::Sqrt => write!(f, "SQRT"),
            Function::Sqrtpi => write!(f, "SQRTPI"),
            Function::Atan2 => write!(f, "ATAN2"),
            Function::Power => write!(f, "POWER"),
            Function::Max => write!(f, "MAX"),
            Function::Min => write!(f, "MIN"),
            Function::Product => write!(f, "PRODUCT"),
            Function::Rand => write!(f, "RAND"),
            Function::Randbetween => write!(f, "RANDBETWEEN"),
            Function::Round => write!(f, "ROUND"),
            Function::Rounddown => write!(f, "ROUNDDOWN"),
            Function::Roundup => write!(f, "ROUNDUP"),
            Function::Sum => write!(f, "SUM"),
            Function::Sumif => write!(f, "SUMIF"),
            Function::Sumifs => write!(f, "SUMIFS"),
            Function::Choose => write!(f, "CHOOSE"),
            Function::Column => write!(f, "COLUMN"),
            Function::Columns => write!(f, "COLUMNS"),
            Function::Index => write!(f, "INDEX"),
            Function::Indirect => write!(f, "INDIRECT"),
            Function::Hlookup => write!(f, "HLOOKUP"),
            Function::Lookup => write!(f, "LOOKUP"),
            Function::Match => write!(f, "MATCH"),
            Function::Offset => write!(f, "OFFSET"),
            Function::Row => write!(f, "ROW"),
            Function::Rows => write!(f, "ROWS"),
            Function::Vlookup => write!(f, "VLOOKUP"),
            Function::Xlookup => write!(f, "XLOOKUP"),
            Function::Concatenate => write!(f, "CONCATENATE"),
            Function::Exact => write!(f, "EXACT"),
            Function::Value => write!(f, "VALUE"),
            Function::T => write!(f, "T"),
            Function::Valuetotext => write!(f, "VALUETOTEXT"),
            Function::Concat => write!(f, "CONCAT"),
            Function::Find => write!(f, "FIND"),
            Function::Left => write!(f, "LEFT"),
            Function::Len => write!(f, "LEN"),
            Function::Lower => write!(f, "LOWER"),
            Function::Mid => write!(f, "MID"),
            Function::Right => write!(f, "RIGHT"),
            Function::Search => write!(f, "SEARCH"),
            Function::Text => write!(f, "TEXT"),
            Function::Trim => write!(f, "TRIM"),
            Function::Unicode => write!(f, "UNICODE"),
            Function::Upper => write!(f, "UPPER"),
            Function::Isnumber => write!(f, "ISNUMBER"),
            Function::Isnontext => write!(f, "ISNONTEXT"),
            Function::Istext => write!(f, "ISTEXT"),
            Function::Islogical => write!(f, "ISLOGICAL"),
            Function::Isblank => write!(f, "ISBLANK"),
            Function::Iserr => write!(f, "ISERR"),
            Function::Iserror => write!(f, "ISERROR"),
            Function::Isna => write!(f, "ISNA"),
            Function::Na => write!(f, "NA"),
            Function::Isref => write!(f, "ISREF"),
            Function::Isodd => write!(f, "ISODD"),
            Function::Iseven => write!(f, "ISEVEN"),
            Function::ErrorType => write!(f, "ERROR.TYPE"),
            Function::Formulatext => write!(f, "FORMULATEXT"),
            Function::Isformula => write!(f, "ISFORMULA"),
            Function::Type => write!(f, "TYPE"),
            Function::Sheet => write!(f, "SHEET"),
            Function::Average => write!(f, "AVERAGE"),
            Function::Averagea => write!(f, "AVERAGEA"),
            Function::Avedev => write!(f, "AVEDEV"),
            Function::Averageif => write!(f, "AVERAGEIF"),
            Function::Averageifs => write!(f, "AVERAGEIFS"),
            Function::Count => write!(f, "COUNT"),
            Function::Counta => write!(f, "COUNTA"),
            Function::Countblank => write!(f, "COUNTBLANK"),
            Function::Countif => write!(f, "COUNTIF"),
            Function::Countifs => write!(f, "COUNTIFS"),
            Function::Maxifs => write!(f, "MAXIFS"),
            Function::Minifs => write!(f, "MINIFS"),
            Function::Geomean => write!(f, "GEOMEAN"),
            Function::Year => write!(f, "YEAR"),
            Function::Day => write!(f, "DAY"),
            Function::Month => write!(f, "MONTH"),
            Function::Eomonth => write!(f, "EOMONTH"),
            Function::Date => write!(f, "DATE"),
            Function::Datedif => write!(f, "DATEDIF"),
            Function::Datevalue => write!(f, "DATEVALUE"),
            Function::Edate => write!(f, "EDATE"),
            Function::Networkdays => write!(f, "NETWORKDAYS"),
            Function::NetworkdaysIntl => write!(f, "NETWORKDAYS.INTL"),
            Function::Time => write!(f, "TIME"),
            Function::Timevalue => write!(f, "TIMEVALUE"),
            Function::Hour => write!(f, "HOUR"),
            Function::Minute => write!(f, "MINUTE"),
            Function::Second => write!(f, "SECOND"),
            Function::Today => write!(f, "TODAY"),
            Function::Now => write!(f, "NOW"),
            Function::Days => write!(f, "DAYS"),
            Function::Days360 => write!(f, "DAYS360"),
            Function::Weekday => write!(f, "WEEKDAY"),
            Function::Weeknum => write!(f, "WEEKNUM"),
            Function::Workday => write!(f, "WORKDAY"),
            Function::WorkdayIntl => write!(f, "WORKDAY.INTL"),
            Function::Yearfrac => write!(f, "YEARFRAC"),
            Function::Isoweeknum => write!(f, "ISOWEEKNUM"),
            Function::Pmt => write!(f, "PMT"),
            Function::Pv => write!(f, "PV"),
            Function::Rate => write!(f, "RATE"),
            Function::Nper => write!(f, "NPER"),
            Function::Fv => write!(f, "FV"),
            Function::Ppmt => write!(f, "PPMT"),
            Function::Ipmt => write!(f, "IPMT"),
            Function::Npv => write!(f, "NPV"),
            Function::Mirr => write!(f, "MIRR"),
            Function::Irr => write!(f, "IRR"),
            Function::Xirr => write!(f, "XIRR"),
            Function::Xnpv => write!(f, "XNPV"),
            Function::Rept => write!(f, "REPT"),
            Function::Textafter => write!(f, "TEXTAFTER"),
            Function::Textbefore => write!(f, "TEXTBEFORE"),
            Function::Textjoin => write!(f, "TEXTJOIN"),
            Function::Substitute => write!(f, "SUBSTITUTE"),
            Function::Ispmt => write!(f, "ISPMT"),
            Function::Rri => write!(f, "RRI"),
            Function::Sln => write!(f, "SLN"),
            Function::Syd => write!(f, "SYD"),
            Function::Nominal => write!(f, "NOMINAL"),
            Function::Effect => write!(f, "EFFECT"),
            Function::Pduration => write!(f, "PDURATION"),
            Function::Tbillyield => write!(f, "TBILLYIELD"),
            Function::Tbillprice => write!(f, "TBILLPRICE"),
            Function::Tbilleq => write!(f, "TBILLEQ"),
            Function::Dollarde => write!(f, "DOLLARDE"),
            Function::Dollarfr => write!(f, "DOLLARFR"),
            Function::Ddb => write!(f, "DDB"),
            Function::Db => write!(f, "DB"),
            Function::Cumprinc => write!(f, "CUMPRINC"),
            Function::Cumipmt => write!(f, "CUMIPMT"),
            Function::Besseli => write!(f, "BESSELI"),
            Function::Besselj => write!(f, "BESSELJ"),
            Function::Besselk => write!(f, "BESSELK"),
            Function::Bessely => write!(f, "BESSELY"),
            Function::Erf => write!(f, "ERF"),
            Function::ErfPrecise => write!(f, "ERF.PRECISE"),
            Function::Erfc => write!(f, "ERFC"),
            Function::ErfcPrecise => write!(f, "ERFC.PRECISE"),
            Function::Bin2dec => write!(f, "BIN2DEC"),
            Function::Bin2hex => write!(f, "BIN2HEX"),
            Function::Bin2oct => write!(f, "BIN2OCT"),
            Function::Dec2Bin => write!(f, "DEC2BIN"),
            Function::Dec2hex => write!(f, "DEC2HEX"),
            Function::Dec2oct => write!(f, "DEC2OCT"),
            Function::Hex2bin => write!(f, "HEX2BIN"),
            Function::Hex2dec => write!(f, "HEX2DEC"),
            Function::Hex2oct => write!(f, "HEX2OCT"),
            Function::Oct2bin => write!(f, "OCT2BIN"),
            Function::Oct2dec => write!(f, "OCT2DEC"),
            Function::Oct2hex => write!(f, "OCT2HEX"),
            Function::Bitand => write!(f, "BITAND"),
            Function::Bitlshift => write!(f, "BITLSHIFT"),
            Function::Bitor => write!(f, "BITOR"),
            Function::Bitrshift => write!(f, "BITRSHIFT"),
            Function::Bitxor => write!(f, "BITXOR"),
            Function::Complex => write!(f, "COMPLEX"),
            Function::Imabs => write!(f, "IMABS"),
            Function::Imaginary => write!(f, "IMAGINARY"),
            Function::Imargument => write!(f, "IMARGUMENT"),
            Function::Imconjugate => write!(f, "IMCONJUGATE"),
            Function::Imcos => write!(f, "IMCOS"),
            Function::Imcosh => write!(f, "IMCOSH"),
            Function::Imcot => write!(f, "IMCOT"),
            Function::Imcsc => write!(f, "IMCSC"),
            Function::Imcsch => write!(f, "IMCSCH"),
            Function::Imdiv => write!(f, "IMDIV"),
            Function::Imexp => write!(f, "IMEXP"),
            Function::Imln => write!(f, "IMLN"),
            Function::Imlog10 => write!(f, "IMLOG10"),
            Function::Imlog2 => write!(f, "IMLOG2"),
            Function::Impower => write!(f, "IMPOWER"),
            Function::Improduct => write!(f, "IMPRODUCT"),
            Function::Imreal => write!(f, "IMREAL"),
            Function::Imsec => write!(f, "IMSEC"),
            Function::Imsech => write!(f, "IMSECH"),
            Function::Imsin => write!(f, "IMSIN"),
            Function::Imsinh => write!(f, "IMSINH"),
            Function::Imsqrt => write!(f, "IMSQRT"),
            Function::Imsub => write!(f, "IMSUB"),
            Function::Imsum => write!(f, "IMSUM"),
            Function::Imtan => write!(f, "IMTAN"),
            Function::Convert => write!(f, "CONVERT"),
            Function::Delta => write!(f, "DELTA"),
            Function::Gestep => write!(f, "GESTEP"),
            Function::Subtotal => write!(f, "SUBTOTAL"),
            Function::Exp => write!(f, "EXP"),
            Function::Fact => write!(f, "FACT"),
            Function::Factdouble => write!(f, "FACTDOUBLE"),
            Function::Sign => write!(f, "SIGN"),
            Function::Radians => write!(f, "RADIANS"),
            Function::Degrees => write!(f, "DEGREES"),
            Function::Int => write!(f, "INT"),
            Function::Even => write!(f, "EVEN"),
            Function::Odd => write!(f, "ODD"),
            Function::Ceiling => write!(f, "CEILING"),
            Function::CeilingMath => write!(f, "CEILING.MATH"),
            Function::CeilingPrecise => write!(f, "CEILING.PRECISE"),
            Function::Floor => write!(f, "FLOOR"),
            Function::FloorMath => write!(f, "FLOOR.MATH"),
            Function::FloorPrecise => write!(f, "FLOOR.PRECISE"),
            Function::IsoCeiling => write!(f, "ISO.CEILING"),
            Function::Mod => write!(f, "MOD"),
            Function::Quotient => write!(f, "QUOTIENT"),
            Function::Mround => write!(f, "MROUND"),
            Function::Trunc => write!(f, "TRUNC"),
            Function::Gcd => write!(f, "GCD"),
            Function::Lcm => write!(f, "LCM"),
            Function::Base => write!(f, "BASE"),
            Function::Decimal => write!(f, "DECIMAL"),
            Function::Roman => write!(f, "ROMAN"),
            Function::Arabic => write!(f, "ARABIC"),
            Function::Combin => write!(f, "COMBIN"),
            Function::Combina => write!(f, "COMBINA"),
            Function::Sumsq => write!(f, "SUMSQ"),
            Function::N => write!(f, "N"),
            Function::Cell => write!(f, "CELL"),
            Function::Info => write!(f, "INFO"),
            Function::Sheets => write!(f, "SHEETS"),
            Function::Daverage => write!(f, "DAVERAGE"),
            Function::Dcount => write!(f, "DCOUNT"),
            Function::Dget => write!(f, "DGET"),
            Function::Dmax => write!(f, "DMAX"),
            Function::Dmin => write!(f, "DMIN"),
            Function::Dsum => write!(f, "DSUM"),
            Function::Dcounta => write!(f, "DCOUNTA"),
            Function::Dproduct => write!(f, "DPRODUCT"),
            Function::Dstdev => write!(f, "DSTDEV"),
            Function::Dvar => write!(f, "DVAR"),
            Function::Dvarp => write!(f, "DVARP"),
            Function::Dstdevp => write!(f, "DSTDEVP"),
            Function::BetaDist => write!(f, "BETA.DIST"),
            Function::BetaInv => write!(f, "BETA.INV"),
            Function::BinomDist => write!(f, "BINOM.DIST"),
            Function::BinomDistRange => write!(f, "BINOM.DIST.RANGE"),
            Function::BinomInv => write!(f, "BINOM.INV"),
            Function::ChisqDist => write!(f, "CHISQ.DIST"),
            Function::ChisqDistRT => write!(f, "CHISQ.DIST.RT"),
            Function::ChisqInv => write!(f, "CHISQ.INV"),
            Function::ChisqInvRT => write!(f, "CHISQ.INV.RT"),
            Function::ChisqTest => write!(f, "CHISQ.TEST"),
            Function::ConfidenceNorm => write!(f, "CONFIDENCE.NORM"),
            Function::ConfidenceT => write!(f, "CONFIDENCE.T"),
            Function::CovarianceP => write!(f, "COVARIANCE.P"),
            Function::CovarianceS => write!(f, "COVARIANCE.S"),
            Function::Devsq => write!(f, "DEVSQ"),
            Function::ExponDist => write!(f, "EXPON.DIST"),
            Function::FDist => write!(f, "F.DIST"),
            Function::FDistRT => write!(f, "F.DIST.RT"),
            Function::FInv => write!(f, "F.INV"),
            Function::FInvRT => write!(f, "F.INV.RT"),
            Function::Fisher => write!(f, "FISHER"),
            Function::FisherInv => write!(f, "FISHERINV"),
            Function::FTest => write!(f, "F.TEST"),
            Function::Gamma => write!(f, "GAMMA"),
            Function::GammaDist => write!(f, "GAMMA.DIST"),
            Function::GammaInv => write!(f, "GAMMA.INV"),
            Function::GammaLn => write!(f, "GAMMALN"),
            Function::GammaLnPrecise => write!(f, "GAMMALN.PRECISE"),
            Function::HypGeomDist => write!(f, "HYPGEOM.DIST"),
            Function::LogNormDist => write!(f, "LOGNORM.DIST"),
            Function::LogNormInv => write!(f, "LOGNORM.INV"),
            Function::NegbinomDist => write!(f, "NEGBINOM.DIST"),
            Function::NormDist => write!(f, "NORM.DIST"),
            Function::NormInv => write!(f, "NORM.INV"),
            Function::NormSdist => write!(f, "NORM.S.DIST"),
            Function::NormSInv => write!(f, "NORM.S.INV"),
            Function::Pearson => write!(f, "PEARSON"),
            Function::Phi => write!(f, "PHI"),
            Function::PoissonDist => write!(f, "POISSON.DIST"),
            Function::Standardize => write!(f, "STANDARDIZE"),
            Function::StDevP => write!(f, "STDEV.P"),
            Function::StDevS => write!(f, "STDEV.S"),
            Function::Stdeva => write!(f, "STDEVA"),
            Function::Stdevpa => write!(f, "STDEVPA"),
            Function::TDist => write!(f, "T.DIST"),
            Function::TDist2T => write!(f, "T.DIST.2T"),
            Function::TDistRT => write!(f, "T.DIST.RT"),
            Function::TInv => write!(f, "T.INV"),
            Function::TInv2T => write!(f, "T.INV.2T"),
            Function::TTest => write!(f, "T.TEST"),
            Function::VarP => write!(f, "VAR.P"),
            Function::VarS => write!(f, "VAR.S"),
            Function::VarpA => write!(f, "VARPA"),
            Function::VarA => write!(f, "VARA"),
            Function::WeibullDist => write!(f, "WEIBULL.DIST"),
            Function::ZTest => write!(f, "Z.TEST"),
            Function::Sumx2my2 => write!(f, "SUMX2MY2"),
            Function::Sumx2py2 => write!(f, "SUMX2PY2"),
            Function::Sumxmy2 => write!(f, "SUMXMY2"),
            Function::Correl => write!(f, "CORREL"),
            Function::Rsq => write!(f, "RSQ"),
            Function::Intercept => write!(f, "INTERCEPT"),
            Function::Slope => write!(f, "SLOPE"),
            Function::Steyx => write!(f, "STEYX"),
            // new ones
            Function::Gauss => write!(f, "GAUSS"),
            Function::Harmean => write!(f, "HARMEAN"),
            Function::Kurt => write!(f, "KURT"),
            Function::Large => write!(f, "LARGE"),
            Function::MaxA => write!(f, "MAXA"),
            Function::Median => write!(f, "MEDIAN"),
            Function::MinA => write!(f, "MINA"),
            Function::RankAvg => write!(f, "RANK.AVG"),
            Function::RankEq => write!(f, "RANK.EQ"),
            Function::Skew => write!(f, "SKEW"),
            Function::SkewP => write!(f, "SKEW.P"),
            Function::Small => write!(f, "SMALL"),
        }
    }
}

/// Documentation for one function
pub struct Documentation {
    pub name: String,
}

impl Model {
    /// Produces documentation for all implemented functions
    pub fn documentation() -> Vec<Documentation> {
        let mut doc = Vec::new();
        for function in Function::into_iter() {
            doc.push(Documentation {
                name: function.to_string(),
            });
        }
        doc
    }

    pub(crate) fn evaluate_function(
        &mut self,
        kind: &Function,
        args: &[Node],
        cell: CellReferenceIndex,
    ) -> CalcResult {
        match kind {
            Function::And => self.fn_and(args, cell),
            Function::False => self.fn_false(args, cell),
            Function::If => self.fn_if(args, cell),
            Function::Iferror => self.fn_iferror(args, cell),
            Function::Ifna => self.fn_ifna(args, cell),
            Function::Ifs => self.fn_ifs(args, cell),
            Function::Not => self.fn_not(args, cell),
            Function::Or => self.fn_or(args, cell),
            Function::Switch => self.fn_switch(args, cell),
            Function::True => self.fn_true(args, cell),
            Function::Xor => self.fn_xor(args, cell),
            Function::Log => self.fn_log(args, cell),
            Function::Log10 => self.fn_log10(args, cell),
            Function::Ln => self.fn_ln(args, cell),
            Function::Sin => self.fn_sin(args, cell),
            Function::Cos => self.fn_cos(args, cell),
            Function::Tan => self.fn_tan(args, cell),
            Function::Asin => self.fn_asin(args, cell),
            Function::Acos => self.fn_acos(args, cell),
            Function::Atan => self.fn_atan(args, cell),
            Function::Sinh => self.fn_sinh(args, cell),
            Function::Cosh => self.fn_cosh(args, cell),
            Function::Tanh => self.fn_tanh(args, cell),
            Function::Asinh => self.fn_asinh(args, cell),
            Function::Acosh => self.fn_acosh(args, cell),
            Function::Atanh => self.fn_atanh(args, cell),
            Function::Pi => self.fn_pi(args, cell),
            Function::Abs => self.fn_abs(args, cell),
            Function::Sqrt => self.fn_sqrt(args, cell),
            Function::Sqrtpi => self.fn_sqrtpi(args, cell),
            Function::Atan2 => self.fn_atan2(args, cell),
            Function::Power => self.fn_power(args, cell),
            Function::Max => self.fn_max(args, cell),
            Function::Min => self.fn_min(args, cell),
            Function::Product => self.fn_product(args, cell),
            Function::Rand => self.fn_rand(args, cell),
            Function::Randbetween => self.fn_randbetween(args, cell),
            Function::Round => self.fn_round(args, cell),
            Function::Rounddown => self.fn_rounddown(args, cell),
            Function::Roundup => self.fn_roundup(args, cell),
            Function::Sum => self.fn_sum(args, cell),
            Function::Sumif => self.fn_sumif(args, cell),
            Function::Sumifs => self.fn_sumifs(args, cell),
            Function::Choose => self.fn_choose(args, cell),
            Function::Column => self.fn_column(args, cell),
            Function::Columns => self.fn_columns(args, cell),
            Function::Index => self.fn_index(args, cell),
            Function::Indirect => self.fn_indirect(args, cell),
            Function::Hlookup => self.fn_hlookup(args, cell),
            Function::Lookup => self.fn_lookup(args, cell),
            Function::Match => self.fn_match(args, cell),
            Function::Offset => self.fn_offset(args, cell),
            Function::Row => self.fn_row(args, cell),
            Function::Rows => self.fn_rows(args, cell),
            Function::Vlookup => self.fn_vlookup(args, cell),
            Function::Xlookup => self.fn_xlookup(args, cell),
            Function::Concatenate => self.fn_concatenate(args, cell),
            Function::Exact => self.fn_exact(args, cell),
            Function::Value => self.fn_value(args, cell),
            Function::T => self.fn_t(args, cell),
            Function::Valuetotext => self.fn_valuetotext(args, cell),
            Function::Concat => self.fn_concat(args, cell),
            Function::Find => self.fn_find(args, cell),
            Function::Left => self.fn_left(args, cell),
            Function::Len => self.fn_len(args, cell),
            Function::Lower => self.fn_lower(args, cell),
            Function::Mid => self.fn_mid(args, cell),
            Function::Right => self.fn_right(args, cell),
            Function::Search => self.fn_search(args, cell),
            Function::Text => self.fn_text(args, cell),
            Function::Trim => self.fn_trim(args, cell),
            Function::Unicode => self.fn_unicode(args, cell),
            Function::Upper => self.fn_upper(args, cell),
            Function::Isnumber => self.fn_isnumber(args, cell),
            Function::Isnontext => self.fn_isnontext(args, cell),
            Function::Istext => self.fn_istext(args, cell),
            Function::Islogical => self.fn_islogical(args, cell),
            Function::Isblank => self.fn_isblank(args, cell),
            Function::Iserr => self.fn_iserr(args, cell),
            Function::Iserror => self.fn_iserror(args, cell),
            Function::Isna => self.fn_isna(args, cell),
            Function::Na => CalcResult::new_error(Error::NA, cell, "".to_string()),
            Function::Isref => self.fn_isref(args, cell),
            Function::Isodd => self.fn_isodd(args, cell),
            Function::Iseven => self.fn_iseven(args, cell),
            Function::ErrorType => self.fn_errortype(args, cell),
            Function::Formulatext => self.fn_formulatext(args, cell),
            Function::Isformula => self.fn_isformula(args, cell),
            Function::Type => self.fn_type(args, cell),
            Function::Sheet => self.fn_sheet(args, cell),
            Function::Average => self.fn_average(args, cell),
            Function::Averagea => self.fn_averagea(args, cell),
            Function::Avedev => self.fn_avedev(args, cell),
            Function::Averageif => self.fn_averageif(args, cell),
            Function::Averageifs => self.fn_averageifs(args, cell),
            Function::Count => self.fn_count(args, cell),
            Function::Counta => self.fn_counta(args, cell),
            Function::Countblank => self.fn_countblank(args, cell),
            Function::Countif => self.fn_countif(args, cell),
            Function::Countifs => self.fn_countifs(args, cell),
            Function::Maxifs => self.fn_maxifs(args, cell),
            Function::Minifs => self.fn_minifs(args, cell),
            Function::Geomean => self.fn_geomean(args, cell),
            Function::Year => self.fn_year(args, cell),
            Function::Day => self.fn_day(args, cell),
            Function::Eomonth => self.fn_eomonth(args, cell),
            Function::Month => self.fn_month(args, cell),
            Function::Date => self.fn_date(args, cell),
            Function::Datedif => self.fn_datedif(args, cell),
            Function::Datevalue => self.fn_datevalue(args, cell),
            Function::Edate => self.fn_edate(args, cell),
            Function::Networkdays => self.fn_networkdays(args, cell),
            Function::NetworkdaysIntl => self.fn_networkdays_intl(args, cell),
            Function::Time => self.fn_time(args, cell),
            Function::Timevalue => self.fn_timevalue(args, cell),
            Function::Hour => self.fn_hour(args, cell),
            Function::Minute => self.fn_minute(args, cell),
            Function::Second => self.fn_second(args, cell),
            Function::Today => self.fn_today(args, cell),
            Function::Now => self.fn_now(args, cell),
            Function::Days => self.fn_days(args, cell),
            Function::Days360 => self.fn_days360(args, cell),
            Function::Weekday => self.fn_weekday(args, cell),
            Function::Weeknum => self.fn_weeknum(args, cell),
            Function::Workday => self.fn_workday(args, cell),
            Function::WorkdayIntl => self.fn_workday_intl(args, cell),
            Function::Yearfrac => self.fn_yearfrac(args, cell),
            Function::Isoweeknum => self.fn_isoweeknum(args, cell),
            Function::Pmt => self.fn_pmt(args, cell),
            Function::Pv => self.fn_pv(args, cell),
            Function::Rate => self.fn_rate(args, cell),
            Function::Nper => self.fn_nper(args, cell),
            Function::Fv => self.fn_fv(args, cell),
            Function::Ppmt => self.fn_ppmt(args, cell),
            Function::Ipmt => self.fn_ipmt(args, cell),
            Function::Npv => self.fn_npv(args, cell),
            Function::Mirr => self.fn_mirr(args, cell),
            Function::Irr => self.fn_irr(args, cell),
            Function::Xirr => self.fn_xirr(args, cell),
            Function::Xnpv => self.fn_xnpv(args, cell),
            Function::Rept => self.fn_rept(args, cell),
            Function::Textafter => self.fn_textafter(args, cell),
            Function::Textbefore => self.fn_textbefore(args, cell),
            Function::Textjoin => self.fn_textjoin(args, cell),
            Function::Substitute => self.fn_substitute(args, cell),
            Function::Ispmt => self.fn_ispmt(args, cell),
            Function::Rri => self.fn_rri(args, cell),
            Function::Sln => self.fn_sln(args, cell),
            Function::Syd => self.fn_syd(args, cell),
            Function::Nominal => self.fn_nominal(args, cell),
            Function::Effect => self.fn_effect(args, cell),
            Function::Pduration => self.fn_pduration(args, cell),
            Function::Tbillyield => self.fn_tbillyield(args, cell),
            Function::Tbillprice => self.fn_tbillprice(args, cell),
            Function::Tbilleq => self.fn_tbilleq(args, cell),
            Function::Dollarde => self.fn_dollarde(args, cell),
            Function::Dollarfr => self.fn_dollarfr(args, cell),
            Function::Ddb => self.fn_ddb(args, cell),
            Function::Db => self.fn_db(args, cell),
            Function::Cumprinc => self.fn_cumprinc(args, cell),
            Function::Cumipmt => self.fn_cumipmt(args, cell),
            Function::Besseli => self.fn_besseli(args, cell),
            Function::Besselj => self.fn_besselj(args, cell),
            Function::Besselk => self.fn_besselk(args, cell),
            Function::Bessely => self.fn_bessely(args, cell),
            Function::Erf => self.fn_erf(args, cell),
            Function::ErfPrecise => self.fn_erfprecise(args, cell),
            Function::Erfc => self.fn_erfc(args, cell),
            Function::ErfcPrecise => self.fn_erfcprecise(args, cell),
            Function::Bin2dec => self.fn_bin2dec(args, cell),
            Function::Bin2hex => self.fn_bin2hex(args, cell),
            Function::Bin2oct => self.fn_bin2oct(args, cell),
            Function::Dec2Bin => self.fn_dec2bin(args, cell),
            Function::Dec2hex => self.fn_dec2hex(args, cell),
            Function::Dec2oct => self.fn_dec2oct(args, cell),
            Function::Hex2bin => self.fn_hex2bin(args, cell),
            Function::Hex2dec => self.fn_hex2dec(args, cell),
            Function::Hex2oct => self.fn_hex2oct(args, cell),
            Function::Oct2bin => self.fn_oct2bin(args, cell),
            Function::Oct2dec => self.fn_oct2dec(args, cell),
            Function::Oct2hex => self.fn_oct2hex(args, cell),
            Function::Bitand => self.fn_bitand(args, cell),
            Function::Bitlshift => self.fn_bitlshift(args, cell),
            Function::Bitor => self.fn_bitor(args, cell),
            Function::Bitrshift => self.fn_bitrshift(args, cell),
            Function::Bitxor => self.fn_bitxor(args, cell),
            Function::Complex => self.fn_complex(args, cell),
            Function::Imabs => self.fn_imabs(args, cell),
            Function::Imaginary => self.fn_imaginary(args, cell),
            Function::Imargument => self.fn_imargument(args, cell),
            Function::Imconjugate => self.fn_imconjugate(args, cell),
            Function::Imcos => self.fn_imcos(args, cell),
            Function::Imcosh => self.fn_imcosh(args, cell),
            Function::Imcot => self.fn_imcot(args, cell),
            Function::Imcsc => self.fn_imcsc(args, cell),
            Function::Imcsch => self.fn_imcsch(args, cell),
            Function::Imdiv => self.fn_imdiv(args, cell),
            Function::Imexp => self.fn_imexp(args, cell),
            Function::Imln => self.fn_imln(args, cell),
            Function::Imlog10 => self.fn_imlog10(args, cell),
            Function::Imlog2 => self.fn_imlog2(args, cell),
            Function::Impower => self.fn_impower(args, cell),
            Function::Improduct => self.fn_improduct(args, cell),
            Function::Imreal => self.fn_imreal(args, cell),
            Function::Imsec => self.fn_imsec(args, cell),
            Function::Imsech => self.fn_imsech(args, cell),
            Function::Imsin => self.fn_imsin(args, cell),
            Function::Imsinh => self.fn_imsinh(args, cell),
            Function::Imsqrt => self.fn_imsqrt(args, cell),
            Function::Imsub => self.fn_imsub(args, cell),
            Function::Imsum => self.fn_imsum(args, cell),
            Function::Imtan => self.fn_imtan(args, cell),
            Function::Convert => self.fn_convert(args, cell),
            Function::Delta => self.fn_delta(args, cell),
            Function::Gestep => self.fn_gestep(args, cell),
            Function::Subtotal => self.fn_subtotal(args, cell),
            Function::Acot => self.fn_acot(args, cell),
            Function::Acoth => self.fn_acoth(args, cell),
            Function::Cot => self.fn_cot(args, cell),
            Function::Coth => self.fn_coth(args, cell),
            Function::Csc => self.fn_csc(args, cell),
            Function::Csch => self.fn_csch(args, cell),
            Function::Sec => self.fn_sec(args, cell),
            Function::Sech => self.fn_sech(args, cell),
            Function::Exp => self.fn_exp(args, cell),
            Function::Fact => self.fn_fact(args, cell),
            Function::Factdouble => self.fn_factdouble(args, cell),
            Function::Sign => self.fn_sign(args, cell),
            Function::Radians => self.fn_radians(args, cell),
            Function::Degrees => self.fn_degrees(args, cell),
            Function::Int => self.fn_int(args, cell),
            Function::Even => self.fn_even(args, cell),
            Function::Odd => self.fn_odd(args, cell),
            Function::Ceiling => self.fn_ceiling(args, cell),
            Function::CeilingMath => self.fn_ceiling_math(args, cell),
            Function::CeilingPrecise => self.fn_ceiling_precise(args, cell),
            Function::Floor => self.fn_floor(args, cell),
            Function::FloorMath => self.fn_floor_math(args, cell),
            Function::FloorPrecise => self.fn_floor_precise(args, cell),
            Function::IsoCeiling => self.fn_iso_ceiling(args, cell),
            Function::Mod => self.fn_mod(args, cell),
            Function::Quotient => self.fn_quotient(args, cell),
            Function::Mround => self.fn_mround(args, cell),
            Function::Trunc => self.fn_trunc(args, cell),
            Function::Gcd => self.fn_gcd(args, cell),
            Function::Lcm => self.fn_lcm(args, cell),
            Function::Base => self.fn_base(args, cell),
            Function::Decimal => self.fn_decimal(args, cell),
            Function::Roman => self.fn_roman(args, cell),
            Function::Arabic => self.fn_arabic(args, cell),
            Function::Combin => self.fn_combin(args, cell),
            Function::Combina => self.fn_combina(args, cell),
            Function::Sumsq => self.fn_sumsq(args, cell),
            Function::N => self.fn_n(args, cell),
            Function::Cell => self.fn_cell(args, cell),
            Function::Info => self.fn_info(args, cell),
            Function::Sheets => self.fn_sheets(args, cell),
            Function::Daverage => self.fn_daverage(args, cell),
            Function::Dcount => self.fn_dcount(args, cell),
            Function::Dget => self.fn_dget(args, cell),
            Function::Dmax => self.fn_dmax(args, cell),
            Function::Dmin => self.fn_dmin(args, cell),
            Function::Dsum => self.fn_dsum(args, cell),
            Function::Dcounta => self.fn_dcounta(args, cell),
            Function::Dproduct => self.fn_dproduct(args, cell),
            Function::Dstdev => self.fn_dstdev(args, cell),
            Function::Dvar => self.fn_dvar(args, cell),
            Function::Dvarp => self.fn_dvarp(args, cell),
            Function::Dstdevp => self.fn_dstdevp(args, cell),
            Function::BetaDist => self.fn_beta_dist(args, cell),
            Function::BetaInv => self.fn_beta_inv(args, cell),
            Function::BinomDist => self.fn_binom_dist(args, cell),
            Function::BinomDistRange => self.fn_binom_dist_range(args, cell),
            Function::BinomInv => self.fn_binom_inv(args, cell),
            Function::ChisqDist => self.fn_chisq_dist(args, cell),
            Function::ChisqDistRT => self.fn_chisq_dist_rt(args, cell),
            Function::ChisqInv => self.fn_chisq_inv(args, cell),
            Function::ChisqInvRT => self.fn_chisq_inv_rt(args, cell),
            Function::ChisqTest => self.fn_chisq_test(args, cell),
            Function::ConfidenceNorm => self.fn_confidence_norm(args, cell),
            Function::ConfidenceT => self.fn_confidence_t(args, cell),
            Function::CovarianceP => self.fn_covariance_p(args, cell),
            Function::CovarianceS => self.fn_covariance_s(args, cell),
            Function::Devsq => self.fn_devsq(args, cell),
            Function::ExponDist => self.fn_expon_dist(args, cell),
            Function::FDist => self.fn_f_dist(args, cell),
            Function::FDistRT => self.fn_f_dist_rt(args, cell),
            Function::FInv => self.fn_f_inv(args, cell),
            Function::FInvRT => self.fn_f_inv_rt(args, cell),
            Function::Fisher => self.fn_fisher(args, cell),
            Function::FisherInv => self.fn_fisher_inv(args, cell),
            Function::FTest => self.fn_f_test(args, cell),
            Function::Gamma => self.fn_gamma(args, cell),
            Function::GammaDist => self.fn_gamma_dist(args, cell),
            Function::GammaInv => self.fn_gamma_inv(args, cell),
            Function::GammaLn => self.fn_gamma_ln(args, cell),
            Function::GammaLnPrecise => self.fn_gamma_ln_precise(args, cell),
            Function::HypGeomDist => self.fn_hyp_geom_dist(args, cell),
            Function::LogNormDist => self.fn_log_norm_dist(args, cell),
            Function::LogNormInv => self.fn_log_norm_inv(args, cell),
            Function::NegbinomDist => self.fn_negbinom_dist(args, cell),
            Function::NormDist => self.fn_norm_dist(args, cell),
            Function::NormInv => self.fn_norm_inv(args, cell),
            Function::NormSdist => self.fn_norm_s_dist(args, cell),
            Function::NormSInv => self.fn_norm_s_inv(args, cell),
            Function::Pearson => self.fn_pearson(args, cell),
            Function::Phi => self.fn_phi(args, cell),
            Function::PoissonDist => self.fn_poisson_dist(args, cell),
            Function::Standardize => self.fn_standardize(args, cell),
            Function::StDevP => self.fn_stdev_p(args, cell),
            Function::StDevS => self.fn_stdev_s(args, cell),
            Function::Stdeva => self.fn_stdeva(args, cell),
            Function::Stdevpa => self.fn_stdevpa(args, cell),
            Function::TDist => self.fn_t_dist(args, cell),
            Function::TDist2T => self.fn_t_dist_2t(args, cell),
            Function::TDistRT => self.fn_t_dist_rt(args, cell),
            Function::TInv => self.fn_t_inv(args, cell),
            Function::TInv2T => self.fn_t_inv_2t(args, cell),
            Function::TTest => self.fn_t_test(args, cell),
            Function::VarP => self.fn_var_p(args, cell),
            Function::VarS => self.fn_var_s(args, cell),
            Function::VarpA => self.fn_varpa(args, cell),
            Function::VarA => self.fn_vara(args, cell),
            Function::WeibullDist => self.fn_weibull_dist(args, cell),
            Function::ZTest => self.fn_z_test(args, cell),
            Function::Sumx2my2 => self.fn_sumx2my2(args, cell),
            Function::Sumx2py2 => self.fn_sumx2py2(args, cell),
            Function::Sumxmy2 => self.fn_sumxmy2(args, cell),
            Function::Correl => self.fn_correl(args, cell),
            Function::Rsq => self.fn_rsq(args, cell),
            Function::Intercept => self.fn_intercept(args, cell),
            Function::Slope => self.fn_slope(args, cell),
            Function::Steyx => self.fn_steyx(args, cell),
            Function::Gauss => self.fn_gauss(args, cell),
            Function::Harmean => self.fn_harmean(args, cell),
            Function::Kurt => self.fn_kurt(args, cell),
            Function::Large => self.fn_large(args, cell),
            Function::MaxA => self.fn_maxa(args, cell),
            Function::Median => self.fn_median(args, cell),
            Function::MinA => self.fn_mina(args, cell),
            Function::RankAvg => self.fn_rank_avg(args, cell),
            Function::RankEq => self.fn_rank_eq(args, cell),
            Function::Skew => self.fn_skew(args, cell),
            Function::SkewP => self.fn_skew_p(args, cell),
            Function::Small => self.fn_small(args, cell),
        }
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::unwrap_used)]
    use std::{
        fs::File,
        io::{BufRead, BufReader},
    };

    use crate::functions::Function;

    #[test]
    fn function_iterator() {
        // This checks that the number of functions in the enum is the same
        // as the number of functions in the Iterator.

        // This is tricky. In Rust we cannot loop over all the members of an enum.
        // There are alternatives like using an external crate like strum.
        // But I am not in the mood for that.

        // What we do here is read this file , extract the functions in the enum
        // and check they are the same as in the iterator
        let file = File::open("src/functions/mod.rs").unwrap();
        let reader = BufReader::new(file);
        let mut start = false;
        let mut list = Vec::new();

        for line in reader.lines() {
            let text = line.unwrap();
            let text = text.trim().trim_end_matches(',');
            if text == "pub enum Function {" {
                start = true;
                continue;
            }
            if start {
                if text == "}" {
                    break;
                }
                if text.starts_with("//") {
                    // skip comments
                    continue;
                }
                if text.is_empty() {
                    // skip empty lines
                    continue;
                }
                list.push(text.to_owned());
            }
        }
        // We make a list with their functions names, but we escape ".": ERROR.TYPE => ERRORTYPE
        let iter_list = Function::into_iter()
            .map(|f| format!("{f}").replace('.', ""))
            .collect::<Vec<_>>();

        let len = iter_list.len();

        assert_eq!(list.len(), len);
        // We still need to check there are no duplicates. This will fail if a function in iter_list
        // is included twice and one is missing
        for function in list {
            assert!(iter_list.contains(&function.to_uppercase()));
        }
    }
}
