// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
// 
// Copyright 2008-2016 Conrad Sanderson (https://conradsanderson.id.au)
// Copyright 2008-2016 National ICT Australia (NICTA)
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// ------------------------------------------------------------------------
// 
// This file includes portions of SuperLU 7.0 software,
// licensed under the following conditions.
// 
// Copyright (c) 2003, The Regents of the University of California, through
// Lawrence Berkeley National Laboratory (subject to receipt of any required 
// approvals from U.S. Dept. of Energy) 
// 
// All rights reserved. 
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met: 
// 
// (1) Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. 
// (2) Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution. 
// (3) Neither the name of Lawrence Berkeley National Laboratory, U.S. Dept. of
// Energy nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// ------------------------------------------------------------------------


#if defined(ARMA_USE_SUPERLU)

#undef ARMA_SLU_HEADERS_FOUND

// Since we need to support float, double, cx_float and cx_double,
// as well as preserve the sanity of the user,
// we cannot simply include all the SuperLU headers due to their messy state
// (duplicate definitions, pollution of global namespace, bizarro defines).
// As such we are forced to include only a subset of the headers
// and manually specify a few SuperLU structures and function prototypes.
//
// CAVEAT:
// This code requires SuperLU version 7.0, and assumes that newer 7.x versions have no API changes.

namespace arma
{
namespace superlu
  {
  // superlu_config.h uses either int or int64_t as int_t
  
  #if defined(ARMA_SUPERLU_64BIT_INT)
    #if defined(INT64_MAX)
      typedef std::int64_t int_t;
    #else
      typedef long long    int_t;
    #endif
  #else
      typedef int          int_t;
  #endif
  }
}

#if defined(ARMA_USE_SUPERLU_HEADERS) || defined(ARMA_SUPERLU_INCLUDE_DIR)

namespace arma
{
namespace superlu
  {
  // Include supermatrix.h.  This gives us SuperMatrix.
  // Put it in the superlu namespace.
  // For versions of SuperLU I am familiar with, supermatrix.h does not include any other files.
  // Therefore, putting it in the superlu namespace is reasonably safe.
  // This same reasoning is true for superlu_enum_consts.h.
  
  #undef ARMA_SLU_HEADER_A
  #undef ARMA_SLU_HEADER_B
  
  #if defined(ARMA_SUPERLU_INCLUDE_DIR)
    #undef ARMA_SLU_STR1
    #undef ARMA_SLU_STR2
    
    #define ARMA_SLU_STR1(x) x
    #define ARMA_SLU_STR2(x) ARMA_SLU_STR1(x)
    
    #define ARMA_SLU_HEADER_A ARMA_SLU_STR2(ARMA_SUPERLU_INCLUDE_DIR)ARMA_SLU_STR2(supermatrix.h)
    #define ARMA_SLU_HEADER_B ARMA_SLU_STR2(ARMA_SUPERLU_INCLUDE_DIR)ARMA_SLU_STR2(superlu_enum_consts.h)
  #else
    #define ARMA_SLU_HEADER_A supermatrix.h
    #define ARMA_SLU_HEADER_B superlu_enum_consts.h
  #endif
  
  #if defined(__has_include)
    #if __has_include(ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_A)) && __has_include(ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_B))
      #include ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_A)
      #include ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_B)
      #define  ARMA_SLU_HEADERS_FOUND
    #endif
  #else
    #include ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_A)
    #include ARMA_INCFILE_WRAP(ARMA_SLU_HEADER_B)
    #define  ARMA_SLU_HEADERS_FOUND
  #endif
  
  #undef ARMA_SLU_STR1
  #undef ARMA_SLU_STR2
  
  #undef ARMA_SLU_HEADER_A
  #undef ARMA_SLU_HEADER_B
  
  #if defined(ARMA_SLU_HEADERS_FOUND)
    
    typedef struct
      {
      int*    panel_histo;
      double* utime;
      float*  ops;        // NOTE: orig definition is flops_t* ops, where flops_t = float
      int     TinyPivots;
      int     RefineSteps;
      int     expansions;
      } SuperLUStat_t;
    
    typedef struct
      {
      fact_t        Fact;
      yes_no_t      Equil;
      colperm_t     ColPerm;
      trans_t       Trans;
      IterRefine_t  IterRefine;
      double        DiagPivotThresh;
      yes_no_t      SymmetricMode;
      yes_no_t      PivotGrowth;
      yes_no_t      ConditionNumber;
      rowperm_t     RowPerm;
      int           ILU_DropRule;
      double        ILU_DropTol;
      double        ILU_FillFactor;
      norm_t        ILU_Norm;
      double        ILU_FillTol;
      milu_t        ILU_MILU;
      double        ILU_MILU_Dim;
      yes_no_t      ParSymbFact;
      yes_no_t      ReplaceTinyPivot;
      yes_no_t      SolveInitialized;
      yes_no_t      RefineInitialized;
      yes_no_t      PrintStat;
      int           nnzL, nnzU;
      int           num_lookaheads;
      yes_no_t      lookahead_etree;
      yes_no_t      SymPattern;
      } superlu_options_t;
    
    typedef struct
      {
      float for_lu;
      float total_needed;
      } mem_usage_t;
    
    typedef struct e_node
      {
      int_t size;
      void* mem;
      } ExpHeader;
    
    typedef struct
      {
      int_t size;
      int_t used;
      int_t top1;
      int_t top2;
      void* array;
      } LU_stack_t;
    
    typedef struct
      {
      int*       xsup;
      int*       supno;   
      int_t*     lsub;
      int_t*     xlsub;
      void*      lusup;
      int_t*     xlusup;
      void*      ucol;
      int_t*     usub;
      int_t*     xusub;
      int_t      nzlmax;
      int_t      nzumax;
      int_t      nzlumax;
      int        n;
      LU_space_t MemModel;
      int        num_expansions;
      ExpHeader* expanders;
      LU_stack_t stack;
      } GlobalLU_t;
    
  #endif
  }
}


#endif

#if defined(ARMA_USE_SUPERLU_HEADERS) && !defined(ARMA_SLU_HEADERS_FOUND)
  #undef ARMA_USE_SUPERLU
  #pragma message ("WARNING: use of SuperLU disabled; required headers not found")
#endif

#endif



#if defined(ARMA_USE_SUPERLU) && !defined(ARMA_SLU_HEADERS_FOUND)

// Not using any SuperLU headers, so define all required enums and structs.

#if defined(ARMA_SUPERLU_INCLUDE_DIR)
  #pragma message ("WARNING: SuperLU headers not found; using built-in definitions")
#endif

namespace arma
{
namespace superlu
  {
  typedef enum
    {
    SLU_NC,
    SLU_NCP,
    SLU_NR,
    SLU_SC,
    SLU_SCP,
    SLU_SR,
    SLU_DN,
    SLU_NR_loc
    } Stype_t;
  
  typedef enum
    {
    SLU_S,
    SLU_D,
    SLU_C,
    SLU_Z
    } Dtype_t;
  
  typedef enum
    {
    SLU_GE,
    SLU_TRLU,
    SLU_TRUU,
    SLU_TRL,
    SLU_TRU,
    SLU_SYL,
    SLU_SYU,
    SLU_HEL,
    SLU_HEU
    } Mtype_t;
  
  typedef struct
    {
    Stype_t Stype;
    Dtype_t Dtype;
    Mtype_t Mtype;
    int_t   nrow;
    int_t   ncol;
    void*   Store;
    } SuperMatrix;
  
  typedef struct
    {
    int*    panel_histo;
    double* utime;
    float*  ops;        // NOTE: orig definition is flops_t* ops, where flops_t = float
    int     TinyPivots;
    int     RefineSteps;
    int     expansions;
    } SuperLUStat_t;
  
  typedef enum {NO, YES}                                                yes_no_t;
  typedef enum {DOFACT, SamePattern, SamePattern_SameRowPerm, FACTORED} fact_t;
  typedef enum {NOROWPERM, LargeDiag_MC64, LargeDiag_HWPM, MY_PERMR}    rowperm_t;
  typedef enum {NATURAL, MMD_ATA, MMD_AT_PLUS_A, COLAMD,
                METIS_AT_PLUS_A, PARMETIS, METIS_ATA, ZOLTAN, MY_PERMC} colperm_t;
  typedef enum {NOTRANS, TRANS, CONJ}                                   trans_t;
  typedef enum {NOREFINE, SLU_SINGLE=1, SLU_DOUBLE, SLU_EXTRA}          IterRefine_t;
  typedef enum {SYSTEM, USER}                                           LU_space_t;
  typedef enum {ONE_NORM, TWO_NORM, INF_NORM}			        norm_t;
  typedef enum {SILU, SMILU_1, SMILU_2, SMILU_3}			milu_t;
  
  typedef struct
    {
    fact_t        Fact;
    yes_no_t      Equil;
    colperm_t     ColPerm;
    trans_t       Trans;
    IterRefine_t  IterRefine;
    double        DiagPivotThresh;
    yes_no_t      SymmetricMode;
    yes_no_t      PivotGrowth;
    yes_no_t      ConditionNumber;
    rowperm_t     RowPerm;
    int           ILU_DropRule;
    double        ILU_DropTol;
    double        ILU_FillFactor;
    norm_t        ILU_Norm;
    double        ILU_FillTol;
    milu_t        ILU_MILU;
    double        ILU_MILU_Dim;
    yes_no_t      ParSymbFact;
    yes_no_t      ReplaceTinyPivot;
    yes_no_t      SolveInitialized;
    yes_no_t      RefineInitialized;
    yes_no_t      PrintStat;
    int           nnzL, nnzU;
    int           num_lookaheads;
    yes_no_t      lookahead_etree;
    yes_no_t      SymPattern;
    } superlu_options_t;
  
  typedef struct
    {
    float for_lu;
    float total_needed;
    } mem_usage_t;
  
  typedef struct
    {
    int_t  nnz;
    void*  nzval;
    int_t* rowind;
    int_t* colptr;
    } NCformat;
  
  typedef struct
    {
    int_t lda;
    void* nzval;
    } DNformat;
  
  typedef struct e_node
    {
    int_t size;
    void* mem;
    } ExpHeader;
  
  typedef struct
    {
    int_t size;
    int_t used;
    int_t top1;
    int_t top2;
    void* array;
    } LU_stack_t;
  
  typedef struct
    {
    int*       xsup;
    int*       supno;   
    int_t*     lsub;
    int_t*     xlsub;
    void*      lusup;
    int_t*     xlusup;
    void*      ucol;
    int_t*     usub;
    int_t*     xusub;
    int_t      nzlmax;
    int_t      nzumax;
    int_t      nzlumax;
    int        n;
    LU_space_t MemModel;
    int        num_expansions;
    ExpHeader* expanders;
    LU_stack_t stack;
    } GlobalLU_t;
  }
}

#undef ARMA_SLU_HEADERS_FOUND

#endif
