// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
//
// Copyright 2008-2016 Conrad Sanderson (http://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
// http://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 5.2 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 suport 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 5.2,
// and assumes that newer 5.x versions will have no API changes.

namespace arma {
namespace superlu {
// slu_*defs.h has int typedefed to int_t.
// I'll just write it as int for simplicity, where I can, but supermatrix.h needs int_t.
typedef int int_t;
}  // namespace superlu
}  // namespace arma

#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 slu 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;
  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 size;
  void* mem;
} ExpHeader;

typedef struct {
  int size;
  int used;
  int top1;
  int top2;
  void* array;
} LU_stack_t;

typedef struct {
  int* xsup;
  int* supno;
  int* lsub;
  int* xlsub;
  void* lusup;
  int* xlusup;
  void* ucol;
  int* usub;
  int* xusub;
  int nzlmax;
  int nzumax;
  int nzlumax;
  int n;
  LU_space_t MemModel;
  int num_expansions;
  ExpHeader* expanders;
  LU_stack_t stack;
} GlobalLU_t;

#endif
}  // namespace superlu
}  // namespace arma

#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;
  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, MY_PERMR } rowperm_t;
typedef enum {
  NATURAL,
  MMD_ATA,
  MMD_AT_PLUS_A,
  COLAMD,
  METIS_AT_PLUS_A,
  PARMETIS,
  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 size;
  void* mem;
} ExpHeader;

typedef struct {
  int size;
  int used;
  int top1;
  int top2;
  void* array;
} LU_stack_t;

typedef struct {
  int* xsup;
  int* supno;
  int* lsub;
  int* xlsub;
  void* lusup;
  int* xlusup;
  void* ucol;
  int* usub;
  int* xusub;
  int nzlmax;
  int nzumax;
  int nzlumax;
  int n;
  LU_space_t MemModel;
  int num_expansions;
  ExpHeader* expanders;
  LU_stack_t stack;
} GlobalLU_t;
}  // namespace superlu
}  // namespace arma

#undef ARMA_SLU_HEADERS_FOUND

#endif
