/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef dirty_08c91b95_68e8_437c_b721_3ad67fdc25ac_h
#define dirty_08c91b95_68e8_437c_b721_3ad67fdc25ac_h

#include <gslib/std.h>
#include <ariel/type.h>

__ariel_begin__

class dirty_list
{
public:
    typedef list<rect> rect_list;
    typedef rect_list::iterator iterator;
    typedef rect_list::const_iterator const_iterator;

protected:
    int         _width, _height;
    int         _cap;
    bool        _whole;

private:
    rect_list   _rclist;

public:
    dirty_list();
    dirty_list(int w, int h);
    int size() const { return (int)_rclist.size(); }
    void clear();
    void set_dimension(int w, int h);
    void add(rect rc);
    void set_whole() { _whole = true;}
    bool is_whole() const { return _whole; }
    bool is_dirty(const rect& rc) const;
    iterator begin() { return _rclist.begin(); }
    const_iterator begin() const { return _rclist.begin(); }
    iterator end() { return _rclist.end(); }
    const_iterator end() const { return _rclist.end(); }
};

__ariel_end__

#endif


#pragma once

#include <gsl/gsl>

struct ScanWordResult;
struct GlyphVertex2d;
struct TigFont;
struct TigTextStyle;
struct TigRect;
struct TigFontMetrics;
using namespace gsl;

namespace gfx {
	class RenderingDevice;
	class ShapeRenderer2d;
	class TextEngine;
}

class FontRenderer {
public:
	explicit FontRenderer(gfx::RenderingDevice& g);
	~FontRenderer();

	void RenderRun(cstring_span<> text,
		int x,
		int y,
		const TigRect& bounds,
		TigTextStyle& style,
		const TigFont& font);

private:

	void RenderGlyphs(const GlyphVertex2d* vertices2d, int textureId, int glyphCount);
	static void Rotate2d(float& x, float& y,
		float rotCos, float rotSin,
		float centerX, float centerY);

	struct Impl;
	std::unique_ptr<Impl> mImpl;

};

/*
Separates a block of text given flags into words split up
on lines and renders them.
*/
class TextLayouter {
	friend class FontRenderer;
public:
	TextLayouter(gfx::RenderingDevice& device, gfx::ShapeRenderer2d& shapeRenderer);
	~TextLayouter();

	void LayoutAndDraw(gsl::cstring_span<> text, const TigFont &font, TigRect& extents, TigTextStyle& style);

	void Measure(const TigFont &font, const TigTextStyle &style, TigFontMetrics &metrics);

private:
	void DrawBackgroundOrOutline(const TigRect& rect, const TigTextStyle& style);
	static int GetGlyphIdx(char ch, const char *text);
	ScanWordResult ScanWord(const char* text,
		int firstIdx,
		int textLength,
		int tabWidth,
		bool lastLine,
		const TigFont& font,
		const TigTextStyle& style,
		int remainingSpace);
	std::pair<int, int> TextLayouter::MeasureCharRun(cstring_span<> text,
		const TigTextStyle& style,
		const TigRect& extents,
		int extentsWidth,
		const TigFont& font,
		int linePadding,
		bool lastLine);
	bool HasMoreText(cstring_span<> text, int tabWidth);

	void LayoutAndDrawVanilla(gsl::cstring_span<> text,
		const TigFont &font,
		TigRect& extents,
		TigTextStyle& style);

	void MeasureVanilla(const TigFont &font,
		const TigTextStyle &style,
		TigFontMetrics &metrics) const;

	uint32_t MeasureVanillaLine(const TigFont &font, const TigTextStyle &style, const char *text) const;
	uint32_t MeasureVanillaParagraph(const TigFont &font, const TigTextStyle &style, const char *text) const;
	uint32_t CountLinesVanilla(uint32_t maxWidth, uint32_t maxLines, const char *text, const TigFont &font, const TigTextStyle &style) const;

	static const char *sEllipsis;
	gfx::TextEngine &mTextEngine;
	FontRenderer mRenderer;
	std::unique_ptr<class FontsMapping> mMapping;
	gfx::ShapeRenderer2d& mShapeRenderer;
};


#include "indexes/utils/Utils.h"

#include <doctest/doctest.h>
#include <gsl/span>

#include <inttypes.h>
#include <limits>
#include <map>
#include <random>
#include <string>
#include <thread>
#include <vector>

enum class ConcurrentMapTestWorkload {
  WL_CONTENTED,
  WL_CONTENTED_SWAP,
  WL_RANDOM,
};

enum class LookupType {
  LT_DEFAULT,
  LT_CUSTOM,
};

std::vector<int64_t> generateUniqueValues(int num_threads, int perthread_count,
                                          ConcurrentMapTestWorkload workload);

template <typename MapType> void MixedMapTest() {
  indexes::utils::ThreadRegistry::RegisterThread();

  MapType map;

  int num_operations = 1024 * 1024;
  int cardinality = num_operations * 0.1;

  constexpr auto INSERT_OP = 1;
  constexpr auto LOOKUP_OP = 2;
  constexpr auto DELETE_OP = 3;

  std::random_device r;
  std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
  std::mt19937 rnd(seed);

  std::uniform_int_distribution<int> key_dist{1, cardinality};
  std::uniform_int_distribution<int> val_dist;
  std::uniform_int_distribution<> op_dist{INSERT_OP, DELETE_OP};

  std::map<int, int> key_values;

  for (int i = 0; i < num_operations; i++) {
    const auto key = key_dist(rnd);
    const auto op = op_dist(rnd);
    int val;

    switch (op) {
    case INSERT_OP: {
      val = val_dist(rnd);

      if (key_values.count(key)) {
        REQUIRE(*map.Search(key) == key_values[key]);
        REQUIRE(*map.Update(key, val) == key_values[key]);
      } else {
        REQUIRE(map.Insert(key, val) == true);
        REQUIRE(*map.Search(key) == val);
      }

      key_values[key] = val;
      break;
    }

    case LOOKUP_OP: {
      if (key_values.count(key))
        REQUIRE(*map.Search(key) == key_values[key]);
      else
        REQUIRE(map.Search(key).has_value() == false);

      break;
    }

    case DELETE_OP: {
      if (key_values.count(key)) {
        REQUIRE(*map.Search(key) == key_values[key]);
        REQUIRE(*map.Delete(key) == key_values[key]);
        key_values.erase(key);
      } else {
        REQUIRE(map.Delete(key).has_value() == false);
      }
      break;
    }

    default:
      continue;
    }
  }

  REQUIRE(map.size() == key_values.size());

  for (const auto &kv : key_values) {
    REQUIRE(*map.Delete(kv.first) == kv.second);
  }

  REQUIRE(map.size() == 0);

  indexes::utils::ThreadRegistry::UnregisterThread();
}

template <typename MapType, LookupType LkType, typename LookupOp>
static void lookup_worker(MapType &map, gsl::span<const int64_t> vals,
                          int64_t min_val, int64_t max_val,
                          const LookupOp &op) {
  indexes::utils::ThreadRegistry::RegisterThread();

  if constexpr (LkType == LookupType::LT_DEFAULT) {
    (void)op;
    for (auto val : vals) {
      map.Search(val);
    }
  } else {
    op(map, min_val, max_val, vals.size());
  }
  indexes::utils::ThreadRegistry::UnregisterThread();
}

template <typename MapType>
static void insert_worker(MapType &map, gsl::span<const int64_t> vals) {
  indexes::utils::ThreadRegistry::RegisterThread();

  for (auto val : vals) {
    REQUIRE(map.Insert(val, val) == true);
    REQUIRE(*map.Search(val) == val);
  }

  indexes::utils::ThreadRegistry::UnregisterThread();
}

enum class OpType { INSERT, DELETE, DELETE_AND_INSERT };

template <typename MapType>
static void delete_worker(MapType &map, gsl::span<const int64_t> vals,
                          OpType op) {
  indexes::utils::ThreadRegistry::RegisterThread();

  for (auto val : vals) {
    switch (op) {
    case OpType::DELETE:
      REQUIRE(*map.Delete(val) == val);
      REQUIRE(map.Search(val).has_value() == false);
      break;

    case OpType::DELETE_AND_INSERT:
      REQUIRE(*map.Delete(val) == val);
      REQUIRE(map.Insert(val, val) == true);
      break;

    case OpType::INSERT:
      break;
    }
  }

  indexes::utils::ThreadRegistry::UnregisterThread();
}

template <typename MapType, LookupType LkType, typename LookupOp>
void ConcurrentMapTest(ConcurrentMapTestWorkload workload, LookupOp &&lop) {
  MapType map;
  constexpr int PER_THREAD_OP_COUNT = 256 * 1024;
  const int NUM_THREADS = std::thread::hardware_concurrency();
  const std::vector<int64_t> vals =
      generateUniqueValues(NUM_THREADS, PER_THREAD_OP_COUNT, workload);
  auto [min_it, max_it] = std::minmax_element(vals.begin(), vals.begin());
  int64_t min_val = *min_it, max_val = *max_it;

  indexes::utils::ThreadRegistry::RegisterThread();

  auto run_test = [&](OpType op) {
    std::vector<std::thread> workers;
    int startval = 0;
    int quantum = vals.size() / NUM_THREADS;

    for (int i = 0; i < NUM_THREADS; i++) {
      if (op == OpType::INSERT) {
        workers.emplace_back(
            insert_worker<MapType>, std::ref(map),
            gsl::span<const int64_t>{vals.data() + startval, quantum});
      } else {
        workers.emplace_back(
            delete_worker<MapType>, std::ref(map),
            gsl::span<const int64_t>{vals.data() + startval, quantum}, op);
      }

      startval += quantum;
    }

    workers.emplace_back(lookup_worker<MapType, LkType, LookupOp>,
                         std::ref(map), gsl::span<const int64_t>{vals}, min_val,
                         max_val, std::cref(lop));

    for (auto &worker : workers) {
      worker.join();
    }
  };

  // Insert check
  {
    run_test(OpType::INSERT);

    REQUIRE(map.size() == vals.size());

    for (auto val : vals) {
      REQUIRE(*map.Search(val) == val);
    }
  }

  // Delete And Insert check
  {
    run_test(OpType::DELETE_AND_INSERT);

    for (auto val : vals) {
      REQUIRE(*map.Search(val) == val);
    }

    REQUIRE(map.size() == vals.size());
  }

  // Delete check
  {
    run_test(OpType::DELETE);

    REQUIRE(map.size() == 0);
  }

  indexes::utils::ThreadRegistry::UnregisterThread();
}


/**
 * \copyright
 * Copyright (c) 2015, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

/**************************************************************************/
/* ROCKFLOW - Modul: makros.h
 */
/* Aufgabe:
   Diese Datei wird von allen ROCKFLOW-Modulen (*.c - Dateien !!!)
   importiert. Sie enthaelt globale Preprozessor-Definitionen und
   importiert weitere, (fast) ueberall benoetigte Header.

 */
/**************************************************************************/

#ifndef makros_INC
#define makros_INC

/* Global benoetigte Header */
//#include <stdlib.h>
/* Speicherverwaltung */
//#include <string.h>
#include <string>

/* Zeichenketten */
//#include <float.h>
/* Floating-Point */

/* ROCKFLOW-Version */
// LB: renamed ROCKFLOW_VERSION to OGS_VERSION and moved the #define to
// Base/Configure.h.in. Please set the version in the top-level CMakeLists.txt!!
// (see sources/CMakeLists.txt)

/* Definitionen von Makros zur Steuerung der bedingten Compilierung */
#define SWITCHES
/* Ausgabe der Schalterstellungen zu Beginn des Programms */
#ifdef MSVCPP6
#pragma warning(disable : 4786)
#endif

/* Laufzeitausgaben */
#define TESTTIME
#ifdef TESTTIME
#define TIMER_ROCKFLOW 0
#endif

/**********************************************************************/
/* Speicher */

#ifndef NO_ERROR_CONTROL /* Wird ggf. im Makefile gesetzt */
#define ERROR_CONTROL
/* Fehlertests (Feldgrenzen, Existenz o.ae.), die bei sauberen Netzen und
   einwandfrei funktionierendem Programm nichts bringen und nur Laufzeit
   kosten */
#endif

#define MEMORY_MANAGEMENT_NOT_ANSI_COMPLIANT
/*  Bei einigen Compilern werden
     malloc(0)
     realloc(NULL,xxx)
     realloc(xxx,0)
     free(NULL)
    nicht ANSI-gerecht gehandhabt. Mit diesem Schalter wird
    ANSI-Verhalten gewaehrleistet. */

#define noMEMORY_ALLOCATION_TEST_SUCCESS
/*  Prueft, ob eine Speicheranforderung erfolgreich absolviert wurde */

#define noMEMORY_TEST_IN_TIME /* fuer Versions-Speichertest */
/* Erstellt waehrend der Laufzeit eine Bilanz des allockierten
   und wieder freigegebenen Speichers. Sehr Zeitintensiv !!! */

#define noMEMORY_STR /* fuer Versions-Speichertest */
/* Gibt Informationen bei Memory-Funktionen zur
   Aufrufstellenlokalisation. Funktioniert nur zusammen mit
   MEMORY_TEST_IN_TIME. Sehr Speicherintensiv!!! */

#define noMEMORY_SHOW_USAGE
/* Gibt bei MEMORY_TEST_IN_TIME Informationen ueber jeden
   Malloc/Realloc/Free-Vorgang aus. */

#define noMEMORY_REALLOC
/* Ersetzt Realloc durch Malloc und Free und speichert um */

#ifndef MEMORY_TEST_IN_TIME
#ifdef MEMORY_SHOW_USAGE
#undef MEMORY_SHOW_USAGE
#endif
#ifndef MEMORY_ALLOCATION_TEST_SUCCESS
#ifdef MEMORY_STR
#undef MEMORY_STR
#endif
#endif
#endif

#ifdef MEMORY_STR
#define Malloc(a) MAlloc(a, __FILE__, __LINE__)
#define Free(a) FRee(a, __FILE__, __LINE__)
#define Realloc(a, b) REalloc(a, b, __FILE__, __LINE__)
/* Ersetzt Malloc, Free und Realloc in allen *.c-Dateien durch den
   erweiterten Aufruf mit Dateiname und Zeilennummer */
#endif

/**********************************************************************/
/* Daten */

#define noENABLE_ADT
/* Listen, Baeumen, etc sind dann moeglich */

/* Definitionen der Feldgroessen */
#define ELEM_START_SIZE 200l
/* Minimale Groesse des Elementverzeichnisses */
#define ELEM_INC_SIZE 1000l
/* Bei Erreichen von ELEM_START_SIZE wird das Elementverzeichnis
   automatisch um ELEM_INC_SIZE vergroessert */
#define NODE_START_SIZE 200l
/* Minimale Groesse des Knotenverzeichnisses */
#define NODE_INC_SIZE 1000l
/* Bei Erreichen von NODE_START_SIZE wird das Knotenverzeichnis
   automatisch um NODE_INC_SIZE vergroessert */
#define EDGE_START_SIZE 0l
/* Minimale Groesse des Kantenverzeichnisses */
#define EDGE_INC_SIZE 1000l
/* Bei Erreichen von EDGE_START_SIZE wird das Kantenverzeichnis
   automatisch um EDGE_INC_SIZE vergroessert */
#define PLAIN_START_SIZE 0l
/* Minimale Groesse des Flaechenverzeichnisses */
#define PLAIN_INC_SIZE 1000l
/* Bei Erreichen von PLAIN_START_SIZE wird das Flaechenverzeichnis
   automatisch um PLAIN_INC_SIZE vergroessert */

/**********************************************************************/
/* Protokolle */

/* Definitionen der Dateinamen-Erweiterungen */
#define TEXT_EXTENSION ".rfd"
/* Dateinamen-Erweiterung fuer Text-Eingabedatei */
#define PROTOCOL_EXTENSION ".rfe"
/* Dateinamen-Erweiterung fuer Text-Protokolldatei */
#define RF_INPUT_EXTENSION ".rfi"
/* Dateinamen-Erweiterung fuer RF-Input-Dateien */
#define RF_OUTPUT_EXTENSION ".rfo"
/* Dateinamen-Erweiterung fuer RF-Output-Dateien */
#define RF_MESSAGE_EXTENSION ".msg"
/* Dateinamen-Erweiterung fuer RF-Output-Dateien */
#define RF_SAVE_EXTENSION1 ".sv1"
/* Dateinamen-Erweiterung fuer RF-Sicherheitskopien */
#define RF_SAVE_EXTENSION2 ".sv2"
/* Dateinamen-Erweiterung fuer RF-Sicherheitskopien */
#define MESH_GENERATOR_EXTENSION ".rfm"
/* Dateinamen-Erweiterung fuer Text-Eingabedatei (Netzgenerator) */
#define MESH_GENERATOR_PROTOCOL_EXTENSION ".rfg"
/* Dateinamen-Erweiterung fuer Text-Protokolldatei (Netzgenerator) */
#define INVERSE_EXTENSION ".rfv" /* ah inv */
/* Dateinamen-Erweiterung fuer Text-Eingabedatei (Inverses Modellieren) */
#define INVERSE_PROTOCOL_EXTENSION ".rfp"
/* Dateinamen-Erweiterung fuer Text-Eingabedatei (Inverses Modellieren) */
#define CHEM_REACTION_EXTENSION ".pqc"
/* Dateinamen-Erweiterung fuer Text-Eingabedatei (Chemical reaction) */
#define CHEMAPP_REACTION_EXTENSION ".chm"
#define REACTION_EXTENSION_CHEMAPP ".cap" // DL/SB 11.2008
#define TEC_FILE_EXTENSION ".tec"
#define VTK_FILE_EXTENSION ".vtk" // GK
#define CSV_FILE_EXTENSION ".csv"

#define noTESTFILES
/* RFD-File Datenbank testen */

#define noEXT_RFD
/* Eingabeprotokoll ausfuehrlich kommentieren */
#define EXT_RFD_MIN
/* Eingabeprotokoll kommentieren, nur gefundene Schluesselworte */
#ifdef EXT_RFD
#undef EXT_RFD_MIN
#endif

/* Format der Double-Ausgabe ueber FilePrintDouble --> txtinout */
#define FORMAT_DOUBLE
#define FPD_GESAMT 4
#define FPD_NACHKOMMA 14

/**********************************************************************/
/* C1.2 Numerik */
#define noTESTTAYLOR
/* nur zu Testzwecken: Taylor-Galerkin-Verfahren nach Donea (1D) */

/**********************************************************************/
/* C1.4 Loeser */
#define noNULLE_ERGEBNIS
/* Nullt den Ergebnisvektor vor Aufruf des Loesers; ansonsten wird er
   mit den Ergebniswerten des letzten Zeitschritts vorbelegt. */
#define noSOLVER_SHOW_ERROR
/* Anzeigen des Iterationsfehlers */
#define noSOLVER_SHOW_RESULTS
/* Anzeigen der Iterationswerte */
#define RELATIVE_EPS
/* Abbruchkriterium bei CG-Loesern wird nicht als absolute Schranke
   benutzt, sondern mit der Norm der rechten Seite multipliziert */
/* Benutzte Normen bei Abbruchkriterien der CG-Loeser mit Speichertechnik:
     MVekNorm1 : Spaltensummennorm
     MVekNorm2 : euklidische Norm
     MVekNormMax : Maximumnorm
   Diese Normen muessen hier fuer die verschiedenen Loeser eingetragen
   werden !!!
 */
#define VEKNORM_BICG MVekNorm2
/* Norm fuer SpBICG-Loeser */
#define VEKNORM_BICGSTAB MVekNorm2
/* Norm fuer SpBICGSTAB-Loeser */
#define VEKNORM_QMRCGSTAB MVekNorm2
/* Norm fuer SpQMRCGSTAB-Loeser */
/*ahb*/
#define VEKNORM_CG MVekNorm2
/* Norm fuer SpCG-Loeser */
#define NORM 2
/* Norm fuer alle Objekte */
#if NORM == 0
#define VEKNORM MVekNormMax
#elif NORM == 1
#define VEKNORM MVekNorm1
#else
#define VEKNORM MVekNorm2
#endif
/*ahe*/

/**********************************************************************/
/* C1.9 Adaption */
#define noTEST_ADAPTIV
/* nur zu Testzwecken */
#define noREF_STATIC
/* erlaubt an ungefaehrlichen Stellen statische Variablen in Rekursionen.
   --> refine.c */

/**********************************************************************/
/* C1.10 Grafik */
#define no__RFGRAF
/* Bindet zur Zeit die Grafik-Funktionen unter X11 */

/**********************************************************************/
/* PCS / C++ */
#define PCS_OBJECTS
#define PCS_NUMBER_MAX 30
#define DOF_NUMBER_MAX 6 // JT: max # dof's per process
#define MAX_FLUID_PHASES 2 // JT: max # fluid phases
#define noPCS_NOD
//#define GLI // KR
#define noWINDOWS

/**********************************************************************/
/* Parallelization */
#define noPARALLEL
#define noCHEMAPP // MX
#define noREACTION_ELEMENT
#define noSX
#define noMPI
#define noOPEN_MP
/* Definitionen von Konstanten, die bei manchen Compilern benoetigt werden */
#ifndef NULL
#define NULL ((void*)0)
#endif
#ifndef TRUE
#define TRUE (0 == 0)
#endif
#ifndef FALSE
#define FALSE (1 == 0)
#endif
#ifndef PI
#define PI 3.14159265358979323846
#endif

/* Feste Zahlen fuer Genauigkeitspruefungen etc. */
#define Mdrittel (1.0 / 3.0)
#define MKleinsteZahl DBL_EPSILON
#define MFastNull DBL_MIN
#define MSqrt2Over3 sqrt(2.0 / 3.0)

/* CBLAS oder MKL_CBLAS verwenden? Wenn ja, wo? */
#define noCBLAS
#define noMKL_CBLAS

/* Includes fuer CBLAS oder MKL */
#ifdef MKL_CBLAS
#include <mkl_cblas.h>
#define CBLAS
#else
#ifdef CBLAS
#include <cblas.h>
#endif
#endif

/* Wo verwenden? */
#ifdef CBLAS
#define CBLAS_M2MatVek
#define CBLAS_MSkalarprodukt
#define CBLAS_MMultMatMat
#endif

/* UMF-Pack-Loeser verwenden? */
#ifdef UMFPACK31
#define UMFPACK
#endif
#ifdef UMFPACK40
#define UMFPACK
#endif

// min und max
#define Max(A, B) ((A) > (B) ? (A) : (B))

/*
   #ifndef min
   #define min(A,B) ((A) < (B) ? (A) : (B))
   #endif
   #ifndef max
   #define max(A,B) ((A) > (B) ? (A) : (B))
   #endif
 */
#define MAX_ZEILE 2048
/* max. Laenge einer UCD-Zeile; bei Leseproblemen vergroessern */

// enum DIS_TYPES {CONSTANT,LINEAR};

extern std::string FileName;
extern std::string FilePath; // WW

#define RESET_4410 // H2_ELE test

//---- MPI Parallel --------------
#if defined(USE_MPI) || defined(USE_MPI_PARPROC) || defined(USE_MPI_REGSOIL) || defined(USE_MPI_GEMS) \
    || defined(USE_MPI_BRNS) || defined(USE_MPI_KRC) || defined(USE_PETSC)
extern int mysize; // WW
extern int myrank;
#endif
//---- MPI Parallel --------------

#endif


/* matrix/gsl_matrix_uint.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_UINT_H__
#define __GSL_MATRIX_UINT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_uint.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned int * data;
  gsl_block_uint * block;
  int owner;
} gsl_matrix_uint;

typedef struct
{
  gsl_matrix_uint matrix;
} _gsl_matrix_uint_view;

typedef _gsl_matrix_uint_view gsl_matrix_uint_view;

typedef struct
{
  gsl_matrix_uint matrix;
} _gsl_matrix_uint_const_view;

typedef const _gsl_matrix_uint_const_view gsl_matrix_uint_const_view;

/* Allocation */

gsl_matrix_uint *
gsl_matrix_uint_alloc (const size_t n1, const size_t n2);

gsl_matrix_uint *
gsl_matrix_uint_calloc (const size_t n1, const size_t n2);

gsl_matrix_uint *
gsl_matrix_uint_alloc_from_block (gsl_block_uint * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

gsl_matrix_uint *
gsl_matrix_uint_alloc_from_matrix (gsl_matrix_uint * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

gsl_vector_uint *
gsl_vector_uint_alloc_row_from_matrix (gsl_matrix_uint * m,
                                        const size_t i);

gsl_vector_uint *
gsl_vector_uint_alloc_col_from_matrix (gsl_matrix_uint * m,
                                        const size_t j);

void gsl_matrix_uint_free (gsl_matrix_uint * m);

/* Views */

_gsl_matrix_uint_view
gsl_matrix_uint_submatrix (gsl_matrix_uint * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

_gsl_vector_uint_view
gsl_matrix_uint_row (gsl_matrix_uint * m, const size_t i);

_gsl_vector_uint_view
gsl_matrix_uint_column (gsl_matrix_uint * m, const size_t j);

_gsl_vector_uint_view
gsl_matrix_uint_diagonal (gsl_matrix_uint * m);

_gsl_vector_uint_view
gsl_matrix_uint_subdiagonal (gsl_matrix_uint * m, const size_t k);

_gsl_vector_uint_view
gsl_matrix_uint_superdiagonal (gsl_matrix_uint * m, const size_t k);

_gsl_matrix_uint_view
gsl_matrix_uint_view_array (unsigned int * base,
                             const size_t n1,
                             const size_t n2);

_gsl_matrix_uint_view
gsl_matrix_uint_view_array_with_tda (unsigned int * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

_gsl_matrix_uint_view
gsl_matrix_uint_view_vector (gsl_vector_uint * v,
                              const size_t n1,
                              const size_t n2);

_gsl_matrix_uint_view
gsl_matrix_uint_view_vector_with_tda (gsl_vector_uint * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

_gsl_matrix_uint_const_view
gsl_matrix_uint_const_submatrix (const gsl_matrix_uint * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

_gsl_vector_uint_const_view
gsl_matrix_uint_const_row (const gsl_matrix_uint * m,
                            const size_t i);

_gsl_vector_uint_const_view
gsl_matrix_uint_const_column (const gsl_matrix_uint * m,
                               const size_t j);

_gsl_vector_uint_const_view
gsl_matrix_uint_const_diagonal (const gsl_matrix_uint * m);

_gsl_vector_uint_const_view
gsl_matrix_uint_const_subdiagonal (const gsl_matrix_uint * m,
                                    const size_t k);

_gsl_vector_uint_const_view
gsl_matrix_uint_const_superdiagonal (const gsl_matrix_uint * m,
                                      const size_t k);

_gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_array (const unsigned int * base,
                                   const size_t n1,
                                   const size_t n2);

_gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_array_with_tda (const unsigned int * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

_gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_vector (const gsl_vector_uint * v,
                                    const size_t n1,
                                    const size_t n2);

_gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_vector_with_tda (const gsl_vector_uint * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

unsigned int   gsl_matrix_uint_get(const gsl_matrix_uint * m, const size_t i, const size_t j);
void    gsl_matrix_uint_set(gsl_matrix_uint * m, const size_t i, const size_t j, const unsigned int x);

unsigned int * gsl_matrix_uint_ptr(gsl_matrix_uint * m, const size_t i, const size_t j);
const unsigned int * gsl_matrix_uint_const_ptr(const gsl_matrix_uint * m, const size_t i, const size_t j);

void gsl_matrix_uint_set_zero (gsl_matrix_uint * m);
void gsl_matrix_uint_set_identity (gsl_matrix_uint * m);
void gsl_matrix_uint_set_all (gsl_matrix_uint * m, unsigned int x);

int gsl_matrix_uint_fread (FILE * stream, gsl_matrix_uint * m) ;
int gsl_matrix_uint_fwrite (FILE * stream, const gsl_matrix_uint * m) ;
int gsl_matrix_uint_fscanf (FILE * stream, gsl_matrix_uint * m);
int gsl_matrix_uint_fprintf (FILE * stream, const gsl_matrix_uint * m, const char * format);

int gsl_matrix_uint_memcpy(gsl_matrix_uint * dest, const gsl_matrix_uint * src);
int gsl_matrix_uint_swap(gsl_matrix_uint * m1, gsl_matrix_uint * m2);

int gsl_matrix_uint_swap_rows(gsl_matrix_uint * m, const size_t i, const size_t j);
int gsl_matrix_uint_swap_columns(gsl_matrix_uint * m, const size_t i, const size_t j);
int gsl_matrix_uint_swap_rowcol(gsl_matrix_uint * m, const size_t i, const size_t j);
int gsl_matrix_uint_transpose (gsl_matrix_uint * m);
int gsl_matrix_uint_transpose_memcpy (gsl_matrix_uint * dest, const gsl_matrix_uint * src);

unsigned int gsl_matrix_uint_max (const gsl_matrix_uint * m);
unsigned int gsl_matrix_uint_min (const gsl_matrix_uint * m);
void gsl_matrix_uint_minmax (const gsl_matrix_uint * m, unsigned int * min_out, unsigned int * max_out);

void gsl_matrix_uint_max_index (const gsl_matrix_uint * m, size_t * imax, size_t *jmax);
void gsl_matrix_uint_min_index (const gsl_matrix_uint * m, size_t * imin, size_t *jmin);
void gsl_matrix_uint_minmax_index (const gsl_matrix_uint * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

int gsl_matrix_uint_isnull (const gsl_matrix_uint * m);

int gsl_matrix_uint_add (gsl_matrix_uint * a, const gsl_matrix_uint * b);
int gsl_matrix_uint_sub (gsl_matrix_uint * a, const gsl_matrix_uint * b);
int gsl_matrix_uint_mul_elements (gsl_matrix_uint * a, const gsl_matrix_uint * b);
int gsl_matrix_uint_div_elements (gsl_matrix_uint * a, const gsl_matrix_uint * b);
int gsl_matrix_uint_scale (gsl_matrix_uint * a, const double x);
int gsl_matrix_uint_add_constant (gsl_matrix_uint * a, const double x);
int gsl_matrix_uint_add_diagonal (gsl_matrix_uint * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
int gsl_matrix_uint_get_row(gsl_vector_uint * v, const gsl_matrix_uint * m, const size_t i);
int gsl_matrix_uint_get_col(gsl_vector_uint * v, const gsl_matrix_uint * m, const size_t j);
int gsl_matrix_uint_set_row(gsl_matrix_uint * m, const size_t i, const gsl_vector_uint * v);
int gsl_matrix_uint_set_col(gsl_matrix_uint * m, const size_t j, const gsl_vector_uint * v);

/* inline functions if you are using GCC */

#ifdef HAVE_INLINE
extern inline
unsigned int
gsl_matrix_uint_get(const gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
    }
#endif
  return m->data[i * m->tda + j] ;
}

extern inline
void
gsl_matrix_uint_set(gsl_matrix_uint * m, const size_t i, const size_t j, const unsigned int x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  m->data[i * m->tda + j] = x ;
}

extern inline
unsigned int *
gsl_matrix_uint_ptr(gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (unsigned int *) (m->data + (i * m->tda + j)) ;
}

extern inline
const unsigned int *
gsl_matrix_uint_const_ptr(const gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (const unsigned int *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_UINT_H__ */


/*
  This file is part of p4est.
  p4est is a C library to manage a collection (a forest) of multiple
  connected adaptive quadtrees or octrees in parallel.

  Copyright (C) 2010 The University of Texas System
  Additional copyright (C) 2011 individual authors
  Written by Carsten Burstedde, Lucas C. Wilcox, and Tobin Isaac

  p4est is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  p4est is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with p4est; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef P4_TO_P8
#include <p4est_plex.h>
#include <p4est_extended.h>
#include <p4est_bits.h>
#else
#include <p8est_plex.h>
#include <p8est_extended.h>
#include <p8est_bits.h>
#endif

#ifdef P4EST_WITH_PETSC
#include <petsc.h>
#include <petscdmplex.h>
#include <petscsf.h>

const char          help[] = "test creating DMPlex from " P4EST_STRING "\n";

static void
locidx_to_PetscInt (sc_array_t * array)
{
  sc_array_t         *newarray;
  size_t              zz, count = array->elem_count;
  P4EST_ASSERT (array->elem_size == sizeof (p4est_locidx_t));

  if (sizeof (p4est_locidx_t) == sizeof (PetscInt)) {
    return;
  }

  newarray = sc_array_new_size (sizeof (PetscInt), array->elem_count);
  for (zz = 0; zz < count; zz++) {
    p4est_locidx_t      il = *((p4est_locidx_t *) sc_array_index (array, zz));
    PetscInt           *ip = (PetscInt *) sc_array_index (newarray, zz);

    *ip = (PetscInt) il;
  }

  sc_array_reset (array);
  sc_array_init_size (array, sizeof (PetscInt), count);
  sc_array_copy (array, newarray);
  sc_array_destroy (newarray);
}

static void
coords_double_to_PetscScalar (sc_array_t * array)
{
  sc_array_t         *newarray;
  size_t              zz, count = array->elem_count;
  P4EST_ASSERT (array->elem_size == 3 * sizeof (double));

  if (sizeof (double) == sizeof (PetscScalar)) {
    return;
  }

  newarray = sc_array_new_size (3 * sizeof (PetscScalar), array->elem_count);
  for (zz = 0; zz < count; zz++) {
    double             *id = (double *) sc_array_index (array, zz);
    PetscScalar        *ip = (PetscScalar *) sc_array_index (newarray, zz);

    ip[0] = (PetscScalar) id[0];
    ip[1] = (PetscScalar) id[1];
    ip[2] = (PetscScalar) id[2];
  }

  sc_array_reset (array);
  sc_array_init_size (array, 3 * sizeof (PetscScalar), count);
  sc_array_copy (array, newarray);
  sc_array_destroy (newarray);
}

static void
locidx_pair_to_PetscSFNode (sc_array_t * array)
{
  sc_array_t         *newarray;
  size_t              zz, count = array->elem_count;
  P4EST_ASSERT (array->elem_size == 2 * sizeof (p4est_locidx_t));

  newarray = sc_array_new_size (sizeof (PetscSFNode), array->elem_count);
  for (zz = 0; zz < count; zz++) {
    p4est_locidx_t     *il = (p4est_locidx_t *) sc_array_index (array, zz);
    PetscSFNode        *ip = (PetscSFNode *) sc_array_index (newarray, zz);

    ip->rank = (PetscInt) il[0];
    ip->index = (PetscInt) il[1];
  }

  sc_array_reset (array);
  sc_array_init_size (array, sizeof (PetscSFNode), count);
  sc_array_copy (array, newarray);
  sc_array_destroy (newarray);
}
#endif

#ifndef P4_TO_P8
static int          refine_level = 5;
#else
static int          refine_level = 3;
#endif

static int
refine_fn (p4est_t * p4est, p4est_topidx_t which_tree,
           p4est_quadrant_t * quadrant)
{
  int                 cid;

  if (which_tree == 2 || which_tree == 3) {
    return 0;
  }

  cid = p4est_quadrant_child_id (quadrant);

  if (cid == P4EST_CHILDREN - 1 ||
      (quadrant->x >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2) &&
       quadrant->y >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
#ifdef P4_TO_P8
       && quadrant->z >= P4EST_LAST_OFFSET (P4EST_MAXLEVEL - 2)
#endif
      )) {
    return 1;
  }
  if ((int) quadrant->level >= (refine_level - (int) (which_tree % 3))) {
    return 0;
  }
  if (quadrant->level == 1 && cid == 2) {
    return 1;
  }
  if (quadrant->x == P4EST_QUADRANT_LEN (2) &&
      quadrant->y == P4EST_LAST_OFFSET (2)) {
    return 1;
  }
  if (quadrant->y >= P4EST_QUADRANT_LEN (2)) {
    return 0;
  }

  return 1;
}

static int
refine_tree_one_fn (p4est_t * p4est, p4est_topidx_t which_tree,
                    p4est_quadrant_t * quadrant)
{
  return ! !which_tree;
}

static int
test_forest (int argc, char **argv, p4est_t * p4est, int overlap)
{
  sc_MPI_Comm         mpicomm;
  int                 mpiret;
  int                 mpisize, mpirank;
  sc_array_t         *points_per_dim, *cone_sizes, *cones,
    *cone_orientations, *coords,
    *children, *parents, *childids, *leaves, *remotes;
  p4est_locidx_t      first_local_quad = -1;

  /* initialize MPI */
  mpicomm = p4est->mpicomm;
  mpiret = sc_MPI_Comm_size (mpicomm, &mpisize);
  SC_CHECK_MPI (mpiret);
  mpiret = sc_MPI_Comm_rank (mpicomm, &mpirank);
  SC_CHECK_MPI (mpiret);

  points_per_dim = sc_array_new (sizeof (p4est_locidx_t));
  cone_sizes = sc_array_new (sizeof (p4est_locidx_t));
  cones = sc_array_new (sizeof (p4est_locidx_t));
  cone_orientations = sc_array_new (sizeof (p4est_locidx_t));
  coords = sc_array_new (3 * sizeof (double));
  children = sc_array_new (sizeof (p4est_locidx_t));
  parents = sc_array_new (sizeof (p4est_locidx_t));
  childids = sc_array_new (sizeof (p4est_locidx_t));
  leaves = sc_array_new (sizeof (p4est_locidx_t));
  remotes = sc_array_new (2 * sizeof (p4est_locidx_t));

  p4est_get_plex_data (p4est, P4EST_CONNECT_FULL, (mpisize > 1) ? overlap : 0,
                       &first_local_quad, points_per_dim, cone_sizes, cones,
                       cone_orientations, coords, children, parents, childids,
                       leaves, remotes);

#ifdef P4EST_WITH_PETSC
  {
    PetscErrorCode      ierr;
    DM                  plex, refTree;
    PetscInt            pStart, pEnd;
    PetscSection        parentSection;
    PetscSF             pointSF;
    size_t              zz, count;

    locidx_to_PetscInt (points_per_dim);
    locidx_to_PetscInt (cone_sizes);
    locidx_to_PetscInt (cones);
    locidx_to_PetscInt (cone_orientations);
    coords_double_to_PetscScalar (coords);
    locidx_to_PetscInt (children);
    locidx_to_PetscInt (parents);
    locidx_to_PetscInt (childids);
    locidx_to_PetscInt (leaves);
    locidx_pair_to_PetscSFNode (remotes);

    P4EST_GLOBAL_PRODUCTION ("Begin PETSc routines\n");
    ierr = PetscInitialize (&argc, &argv, 0, help);
    CHKERRQ (ierr);

    ierr = DMPlexCreate (mpicomm, &plex);
    CHKERRQ (ierr);
    ierr = DMSetDimension (plex, P4EST_DIM);
    CHKERRQ (ierr);
    ierr = DMSetCoordinateDim (plex, 3);
    CHKERRQ (ierr);
    ierr = DMPlexCreateFromDAG (plex, P4EST_DIM,
                                (PetscInt *) points_per_dim->array,
                                (PetscInt *) cone_sizes->array,
                                (PetscInt *) cones->array,
                                (PetscInt *) cone_orientations->array,
                                (PetscScalar *) coords->array);
    CHKERRQ (ierr);
    ierr = PetscSFCreate (mpicomm, &pointSF);
    CHKERRQ (ierr);
    ierr =
      DMPlexCreateDefaultReferenceTree (mpicomm, P4EST_DIM, PETSC_FALSE,
                                        &refTree);
    CHKERRQ (ierr);
    ierr = DMPlexSetReferenceTree (plex, refTree);
    CHKERRQ (ierr);
    ierr = DMDestroy (&refTree);
    CHKERRQ (ierr);
    ierr = PetscSectionCreate (mpicomm, &parentSection);
    CHKERRQ (ierr);
    ierr = DMPlexGetChart (plex, &pStart, &pEnd);
    CHKERRQ (ierr);
    ierr = PetscSectionSetChart (parentSection, pStart, pEnd);
    CHKERRQ (ierr);
    count = children->elem_count;
    for (zz = 0; zz < count; zz++) {
      PetscInt            child =
        *((PetscInt *) sc_array_index (children, zz));

      ierr = PetscSectionSetDof (parentSection, child, 1);
      CHKERRQ (ierr);
    }
    ierr = PetscSectionSetUp (parentSection);
    CHKERRQ (ierr);
    ierr =
      DMPlexSetTree (plex, parentSection, (PetscInt *) parents->array,
                     (PetscInt *) childids->array);
    CHKERRQ (ierr);
    ierr = PetscSectionDestroy (&parentSection);
    CHKERRQ (ierr);
    ierr =
      PetscSFSetGraph (pointSF, pEnd - pStart, (PetscInt) leaves->elem_count,
                       (PetscInt *) leaves->array, PETSC_COPY_VALUES,
                       (PetscSFNode *) remotes->array, PETSC_COPY_VALUES);
    CHKERRQ (ierr);
    ierr = DMSetPointSF (plex, pointSF);
    CHKERRQ (ierr);
    ierr = PetscSFDestroy (&pointSF);
    CHKERRQ (ierr);
    ierr = DMViewFromOptions (plex, NULL, "-dm_view");
    CHKERRQ (ierr);
    /* TODO: test with rigid body modes as in plex ex3 */
    ierr = DMDestroy (&plex);
    CHKERRQ (ierr);

    ierr = PetscFinalize ();
    P4EST_GLOBAL_PRODUCTION ("End   PETSc routines\n");
  }
#endif

  sc_array_destroy (points_per_dim);
  sc_array_destroy (cone_sizes);
  sc_array_destroy (cones);
  sc_array_destroy (cone_orientations);
  sc_array_destroy (coords);
  sc_array_destroy (children);
  sc_array_destroy (parents);
  sc_array_destroy (childids);
  sc_array_destroy (leaves);
  sc_array_destroy (remotes);

  return 0;
}

static int
test_big (int argc, char **argv)
{
  sc_MPI_Comm         mpicomm;
  int                 mpiret;
  p4est_t            *p4est;
  p4est_connectivity_t *conn;

  /* initialize MPI */
  mpicomm = sc_MPI_COMM_WORLD;

#ifndef P4_TO_P8
  conn = p4est_connectivity_new_moebius ();
#else
  conn = p8est_connectivity_new_rotcubes ();
#endif
  p4est = p4est_new_ext (mpicomm, conn, 0, 1, 1, 0, NULL, NULL);
  p4est_refine (p4est, 1, refine_fn, NULL);
  p4est_balance (p4est, P4EST_CONNECT_FULL, NULL);
  p4est_partition (p4est, 0, NULL);

  mpiret = test_forest (argc, argv, p4est, 2);
  if (mpiret) {
    return mpiret;
  }

  p4est_destroy (p4est);
  p4est_connectivity_destroy (conn);

  return 0;
}

static int
test_small (int argc, char **argv)
{
  sc_MPI_Comm         mpicomm;
  int                 mpiret;
  p4est_t            *p4est;
  p4est_connectivity_t *conn;

  /* initialize MPI */
  mpicomm = sc_MPI_COMM_WORLD;

#ifndef P4_TO_P8
  conn = p4est_connectivity_new_brick (2, 1, 0, 0);
#else
  conn = p8est_connectivity_new_brick (2, 1, 1, 0, 0, 0);
#endif
  p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);
  p4est_refine (p4est, 0, refine_tree_one_fn, NULL);

  mpiret = test_forest (argc, argv, p4est, 0);
  if (mpiret) {
    return mpiret;
  }

  p4est_partition (p4est, 0, NULL);

  mpiret = test_forest (argc, argv, p4est, 0);
  if (mpiret) {
    return mpiret;
  }

  p4est_destroy (p4est);
  p4est_connectivity_destroy (conn);

  return 0;
}

int
main (int argc, char **argv)
{
  sc_MPI_Comm         mpicomm;
  int                 mpiret;

  /* initialize MPI */
  mpiret = sc_MPI_Init (&argc, &argv);
  SC_CHECK_MPI (mpiret);
  mpicomm = sc_MPI_COMM_WORLD;

  sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT);
  p4est_init (NULL, SC_LP_DEFAULT);

  mpiret = test_small (argc, argv);
  if (mpiret) {
    return mpiret;
  }
  mpiret = test_big (argc, argv);
  if (mpiret) {
    return mpiret;
  }

  sc_finalize ();

  mpiret = sc_MPI_Finalize ();
  SC_CHECK_MPI (mpiret);

  return 0;
}


/* bst/gsl_bst_avl.h
 *
 * Copyright (C) 2018 Patrick Alken
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BST_AVL_H__
#define __GSL_BST_AVL_H__

#include <gsl/gsl_math.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

#ifndef GSL_BST_AVL_MAX_HEIGHT
#define GSL_BST_AVL_MAX_HEIGHT 32
#endif

/* AVL node */
struct gsl_bst_avl_node
{
  struct gsl_bst_avl_node *avl_link[2]; /* subtrees */
  void *avl_data;                       /* pointer to data */
  signed char avl_balance;              /* balance factor */
};

/* tree data structure */
typedef struct
{
  struct gsl_bst_avl_node *avl_root;          /* tree's root */
  gsl_bst_cmp_function *avl_compare;          /* comparison function */
  void *avl_param;                            /* extra argument to |avl_compare| */
  const gsl_bst_allocator *avl_alloc;         /* memory allocator */
  size_t avl_count;                           /* number of items in tree */
  unsigned long avl_generation;               /* generation number */
} gsl_bst_avl_table;

/* AVL traverser structure */
typedef struct
{
  const gsl_bst_avl_table *avl_table;                         /* tree being traversed */
  struct gsl_bst_avl_node *avl_node;                          /* current node in tree */
  struct gsl_bst_avl_node *avl_stack[GSL_BST_AVL_MAX_HEIGHT]; /* all the nodes above |avl_node| */
  size_t avl_height;                                          /* number of nodes in |avl_parent| */
  unsigned long avl_generation;                               /* generation number */
} gsl_bst_avl_traverser;

__END_DECLS

#endif /* __GSL_BST_AVL_H__ */


#pragma once

#include "file_descriptor.h"
#include <filesystem>
#include <gsl/span>
#include <random>

namespace dogbox
{
    inline void create_random_file(std::filesystem::path const &file, uint64_t const size)
    {
        file_descriptor const created = create_file(file).value();
        file_descriptor const random = open_file_for_reading("/dev/urandom").value();
        std::array<std::byte, 0x10000> buffer;
        uint64_t written = 0;
        while (written < size)
        {
            size_t const reading = static_cast<size_t>(std::min<uint64_t>(buffer.size(), (size - written)));
            ssize_t const read_result = read(random.handle, buffer.data(), reading);
            if (read_result < 0)
            {
                TO_DO();
            }
            ssize_t const write_result = write(created.handle, buffer.data(), reading);
            if (write_result < 0)
            {
                TO_DO();
            }
            written += reading;
        }
    }
}


#ifndef __mcas_SERVER_TASK_KEY_FIND_H__
#define __mcas_SERVER_TASK_KEY_FIND_H__

#include <common/logging.h>
#include <gsl/pointers>
#include <unistd.h>
#include <string>

#include "task.h"

namespace mcas
{
/**
 * Key search task.  We limit the number of hops we search so as to bound
 * the worst case execution time.
 *
 */
class Key_find_task : public Shard_task,
                      private common::log_source
{
  static constexpr unsigned MAX_COMPARES_PER_WORK = 5;

 public:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"  // uninitialized _expr, _out_key, _type
  Key_find_task(const std::string& expression,
                const offset_t offset,
                Connection_handler* handler,
                gsl::not_null<component::IKVIndex*> index,
                const unsigned debug_level)
      : Shard_task(handler),
        log_source(debug_level),
        _offset(offset),
        _index(index)
  {
    using namespace component;
    _index->add_ref();

    CPLOG(1, "offset=%lu", offset);
    CPLOG(1,"expr: (%s)", expression.c_str());

    if (expression == "next:") {
      _type = IKVIndex::FIND_TYPE_NEXT;
      _expr = expression.substr(5);
    }
    else if (expression.substr(0, 6) == "regex:") {
      _type = IKVIndex::FIND_TYPE_REGEX;
      _expr = expression.substr(6);
    }
    else if (expression.substr(0, 6) == "exact:") {
      _type = IKVIndex::FIND_TYPE_EXACT;
      _expr = expression.substr(6);
    }
    else if (expression.substr(0, 7) == "prefix:") {
      _type = IKVIndex::FIND_TYPE_PREFIX;
      _expr = expression.substr(7);
    }
    else
      throw Logic_exception("unhandled expression");

  }
#pragma GCC diagnostic pop

  Key_find_task(const Key_find_task&) = delete;
  Key_find_task& operator=(const Key_find_task&) = delete;

  status_t do_work() override
  {
    using namespace component;

    status_t hr;
    try {
      hr = _index->find(_expr, _offset, _type, _offset, _out_key, MAX_COMPARES_PER_WORK);

      if (hr == E_MAX_REACHED) {
        _offset++;
        return component::IKVStore::S_MORE;
      }
      else if (hr == S_OK) {
        CPLOG(2, "matched: (%s)", _out_key.c_str());
        return S_OK;
      }
      else {
        _out_key.clear();
        return hr;
      }
    }
    catch (...) {
      return E_FAIL;
    }

    throw Logic_exception("unexpected code path (hr=%d)", hr);
  }

  const void* get_result() const override { return _out_key.data(); }

  size_t get_result_length() const override { return _out_key.length(); }

  offset_t matched_position() const override { return _offset; }

 private:
  std::string                             _expr;
  std::string                             _out_key;
  component::IKVIndex::find_t             _type;
  offset_t                                _offset;
  component::Itf_ref<component::IKVIndex> _index;
};

}  // namespace mcas
#endif  // __mcas_SERVER_TASK_KEY_FIND_H__


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iniparser.h>
#include "parmt_utils.h"
#include "tdsearch_greens.h"
#include "tdsearch_commands.h"
#ifdef TDSEARCH_USE_INTEL
#include <mkl_blas.h>
#else
#include <cblas.h>
#endif
#include "tdsearch_struct.h"
#include "tdsearch_hudson.h"
#include "ispl/process.h"
#include "iscl/array/array.h"
#include "iscl/fft/fft.h"
#include "iscl/memory/memory.h"
#include "iscl/os/os.h"

static int getPrimaryArrival(const struct sacHeader_struct hdr,
                             double *time, char phaseName[8]);

/*!
 * @brief Reads the generic Green's functions pre-processing files from
 *        the ini file.
 *
 * @param[in] iniFile    Name of ini file.
 * @param[in] nobs       Number of observations.
 *
 * @param[in,out] grns   On input contains the number of observations.
 *                       On exit contains the generic Green's functions
 *                       processing commands for the observations.
 *
 * @result 0 indicates success.
 *
 * @author Ben Baker, ISTI
 *
 * @ingroup tdsearch_greens
 *
 * @bug Add an option to read a processing list file.
 *
 */
int tdsearch_greens_setPreprocessingCommandsFromIniFile(
    const char *iniFile,
    const int nobs,
    struct tdSearchGreens_struct *grns)
{
    dictionary *ini;
    char **cmds;
    const char *s;
    char varname[128];
    size_t lenos;
    int ierr, k, ncmds, ncmdsWork;
    ierr = 0;
    grns->nobs = nobs;
    if (grns->nobs < 1){return 0;}
    if (!os_path_isfile(iniFile))
    {
        fprintf(stderr, "%s: Error ini file %s doesn't exist\n",
                __func__, iniFile);
        return -1;
    }
    ini = iniparser_load(iniFile);
    ncmds = iniparser_getint(ini, "tdSearch:greens:nCommands\0", 0);
    grns->cmds = (struct tdSearchDataProcessingCommands_struct *)
                 calloc((size_t) nobs, //ncmds,
                        sizeof(struct tdSearchDataProcessingCommands_struct));
    //if (!luseProcessingList)
    {
        if (ncmds > 0)
        {
            ncmdsWork = ncmds;
            ncmds = 0;
            cmds = (char **) calloc((size_t) ncmdsWork, sizeof(char *));
            for (k=0; k<ncmdsWork; k++)
            {
                memset(varname, 0, 128*sizeof(char));
                sprintf(varname, "tdSearch:greens:command_%d", k+1);
                s = iniparser_getstring(ini, varname, NULL);
                if (s == NULL){continue;}
                lenos = strlen(s);
                cmds[ncmds] = (char *) calloc(lenos+1, sizeof(char));
                strcpy(cmds[ncmds], s);
                //printf("%s\n", cmds[ncmds]);
                ncmds = ncmds + 1;
            }
            // Attach these to the data processing structure
            for (k=0; k<grns->nobs; k++)
            {
                ierr = tdsearch_greens_attachCommandsToGreens(
                           k, ncmds, (const char **) cmds, grns);
            }
            // Free space
            for (k=0; k<ncmdsWork; k++)
            {
                if (cmds[k] != NULL){free(cmds[k]);}
            }
            free(cmds);
        }
    }
    iniparser_freedict(ini);
    return ierr;
}
//============================================================================//
/*!
 * @brief Attaches the Green's functions processing commands to the Green's
 *        structure.
 *
 * @param[in] iobs      Observation number to attach commands to.
 * @param[in] ncmds     Number of commands.
 * @param[in] cmds      Pre-processing commands for the Green's functions
 *                      corresponding to the iobs'th observation [ncmds].
 *
 * @param[in,out] grns  On input has the Green's functions (depths and t*'s)
 *                      corresponding to the observations.
 *                      On output contains the pre-processing commands
 *                      for Green's functions corresponding to the
 *                      observations.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_attachCommandsToGreens(const int iobs, const int ncmds,
                                           const char **cmds,
                                           struct tdSearchGreens_struct *grns)
{
    int i;
    size_t lenos;
    // Make sure iobs is in bounds
    if (iobs < 0 || iobs >= grns->nobs)
    {
        fprintf(stderr, "%s: Error iobs=%d is out of bounds [0,%d]\n",
                __func__, iobs, grns->nobs);
        return -1;
    }
    // Try to handle space allocation if not already done
    if (grns->cmds == NULL && grns->nobs > 0)
    {
        grns->cmds = (struct tdSearchDataProcessingCommands_struct *)
                     calloc((size_t) grns->nobs,
                        sizeof(struct tdSearchDataProcessingCommands_struct));
    }
    if (grns->cmds == NULL)
    {
        fprintf(stderr, "%s: Error grns->cmds is NULL\n", __func__);
        return -1;
    }
    if (ncmds == 0){return 0;}
    grns->cmds[iobs].ncmds = ncmds;
    grns->cmds[iobs].cmds = (char **) calloc((size_t) ncmds, sizeof(char *));
    if (cmds == NULL){printf("problem 1\n");}
    for (i=0; i<ncmds; i++)
    {
        if (cmds[i] == NULL){printf("problem 2\n");}
        lenos = strlen(cmds[i]);
        grns->cmds[iobs].cmds[i] = (char *) calloc(lenos+1, sizeof(char));
        strcpy(grns->cmds[iobs].cmds[i], cmds[i]);
    }
    return 0;
}
//============================================================================//
/*!
 * @brief Convenience function which returns the index of the Green's
 *        function on the Green's function structure.
 *
 * @param[in] GMT_TERM    Name of the desired Green's function:
 *                        (G11_TERM, G22_TERM, ..., G23_TERM).
 * @param[in] iobs        Desired observation number (C numbering).
 * @param[in] itstar      Desired t* (C numbering).
 * @param[in] idepth      Desired depth (C numbering).
 * @param[in] grns        Contains the number of observations, depths, and t*'s
 *                        on the Green's functions structure.
 *
 * @result Negative indicates failure.  Otherwise, this is the index in
 *         grns.grns corresponding to the desired
 *         (iobs, idepth, itstar, G??_GRNS) coordinate.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_getGreensFunctionIndex(
    const enum prepmtGreens_enum GMT_TERM,
    const int iobs, const int itstar, const int idepth,
    const struct tdSearchGreens_struct grns)
{
    int igx, indx;
    indx =-1;
    igx = (int) GMT_TERM - 1;
    if (igx < 0 || igx > 5)
    {
        fprintf(stderr, "%s: Can't classify Green's functions index\n",
                __func__);
        return indx;
    }
    indx = iobs*(6*grns.ntstar*grns.ndepth)
         + idepth*(6*grns.ntstar)
         + itstar*6
         + igx;
    if (indx >= grns.ngrns)
    {
        fprintf(stdout, "%s: indx out of bounds - segfault is coming\n",
                __func__);
        return -1;
    }
    return indx;
}
//============================================================================//
/*!
 * @brief Convenience function for extracting the:
 *        \$ \{ G_{xx}, G_{yy}, G_{zz}, G_{xy}, G_{xz}, G_{yz} \} \$
 *        Green's functions indices for the observation, t*, and depth.
 *
 * @param[in] iobs      Observation number.
 * @param[in] itstar    t* index.  This is C numbered.
 * @param[in] idepth    Depth index.  This is C numbered.
 * @param[in] grns      Contains the Green's functions.
 * @param[out] indices  Contains the Green's functions indices defining
 *                      the indices that return the:
 *                       \$ \{ G_{xx}, G_{yy}, G_{zz},
 *                             G_{xy}, G_{xz}, G_{yz} \} \$
 *                      for this observation, t*, and depth.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_getGreensFunctionsIndices(
    const int iobs, const int itstar, const int idepth,
    const struct tdSearchGreens_struct grns, int indices[6])
{
    int i, ierr;
    const enum prepmtGreens_enum mtTerm[6] =
       {G11_GRNS, G22_GRNS, G33_GRNS, G12_GRNS, G13_GRNS, G23_GRNS};
    ierr = 0;
    for (i=0; i<6; i++)
    {
        indices[i] = tdsearch_greens_getGreensFunctionIndex(mtTerm[i],
                                                          iobs, itstar, idepth,
                                                          grns);
        if (indices[i] < 0){ierr = ierr + 1;}
    }
    return ierr;
}
//============================================================================//
/*!
 * @brief Releases memory on the Greens functions structure.
 *
 * @param[out] grns   On exit all memory has been freed and variables set to
 *                    0 or NULL.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_free(struct tdSearchGreens_struct *grns)
{
    int i, k;
    if (grns->grns != NULL && grns->ngrns > 0)
    {
        for (k=0; k<grns->ngrns; k++)
        {
            sacio_free(&grns->grns[k]);
        }
        free(grns->grns);
    }
    if (grns->cmds != NULL)
    {
        for (k=0; k<grns->nobs; k++)
        {
            if (grns->cmds[k].cmds != NULL)
            {
                for (i=0; i<grns->cmds[k].ncmds; i++)
                {
                    if (grns->cmds[k].cmds[i] != NULL)
                    {
                        free(grns->cmds[k].cmds[i]);
                        grns->cmds[k].cmds[i] = NULL;
                    }
                }
                free(grns->cmds[k].cmds);
                grns->cmds[k].cmds = NULL;
            }
        }
        free(grns->cmds);
        grns->cmds = NULL;
    }
    if (grns->cmdsGrns != NULL)
    {
        for (k=0; k<grns->ngrns; k++)
        {
            if (grns->cmdsGrns[k].cmds != NULL)
            {
                for (i=0; i<grns->cmdsGrns[k].ncmds; i++)
                {
                    if (grns->cmdsGrns[k].cmds != NULL)
                    {
                        free(grns->cmdsGrns[k].cmds[i]);
                        grns->cmdsGrns[k].cmds[i] = NULL;
                    }
                }
                free(grns->cmdsGrns[k].cmds);
                grns->cmdsGrns[k].cmds = NULL;
             }
        }
        free(grns->cmdsGrns);
        grns->cmdsGrns = NULL;
    }
    memset(grns, 0, sizeof(struct tdSearchGreens_struct));
    return 0;
}
//============================================================================//
/*!
 * @brief Converts the fundamental faults Green's functions to Green's
 *        functions that can be used by tdsearch.
 *
 * @param[in] data    tdSearch data structure.
 * @param[in] ffGrns  fundamental fault Green's functions for every t* and
 *                    depth in the grid search for each observation.
 *
 * @param[out] grns   Contains the Green's functions that can be applied to
 *                    a moment tensor to produce a synthetic for every t*
 *                    and depth in the grid search for each observation.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_ffGreensToGreens(const struct tdSearchData_struct data,
                                     const struct tdSearchHudson_struct ffGrns,
                                     struct tdSearchGreens_struct *grns)
{
    char knetwk[8], kstnm[8], kcmpnm[8], khole[8], phaseName[8],
         phaseNameGrns[8];
    double az, baz, cmpaz, cmpinc, cmpincSEED, dt0, epoch, epochNew,
           evla, evlo, o, pick, pickTime, pickTimeGrns, stel, stla, stlo;
    int i, icomp, id, ierr, idx, indx, iobs, it, kndx, l, npts;
    const char *kcmpnms[6] = {"GXX\0", "GYY\0", "GZZ\0",
                              "GXY\0", "GXZ\0", "GYZ\0"};
    const double xmom = 1.0;     // no confusing `relative' magnitudes
    const double xcps = 1.e-20;  // convert dyne-cm mt to output cm
    const double cm2m = 1.e-2;   // cm to meters
    const double dcm2nm = 1.e+7; // magnitudes intended to be specified in
                                 // Dyne-cm but I work in N-m
    // Given a M0 in Newton-meters get a seismogram in meters
    const double xscal = xmom*xcps*cm2m*dcm2nm;
    const int nTimeVars = 11;
    const enum sacHeader_enum pickVars[11]
       = {SAC_FLOAT_A,
          SAC_FLOAT_T0, SAC_FLOAT_T1, SAC_FLOAT_T2, SAC_FLOAT_T3,
          SAC_FLOAT_T4, SAC_FLOAT_T5, SAC_FLOAT_T6, SAC_FLOAT_T7,
          SAC_FLOAT_T8, SAC_FLOAT_T9};
    //memset(grns, 0, sizeof(struct tdSearchGreens_struct));
    grns->ntstar = ffGrns.ntstar;
    grns->ndepth = ffGrns.ndepth;
    grns->nobs = data.nobs;
    grns->ngrns = 6*grns->ntstar*grns->ndepth*grns->nobs;
    if (grns->ngrns < 1)
    {
        fprintf(stderr, "%s: Error grns is empty\n", __func__);
        return -1;
    }
    grns->grns = (struct sacData_struct *)
                 calloc((size_t) grns->ngrns, sizeof(struct sacData_struct));
    for (iobs=0; iobs<data.nobs; iobs++)
    {
        ierr = 0;
        ierr += sacio_getFloatHeader(SAC_FLOAT_AZ,
                                     data.obs[iobs].header, &az);
        ierr += sacio_getFloatHeader(SAC_FLOAT_BAZ,
                                     data.obs[iobs].header, &baz);
        ierr += sacio_getFloatHeader(SAC_FLOAT_CMPINC,
                                     data.obs[iobs].header, &cmpinc);
        ierr += sacio_getFloatHeader(SAC_FLOAT_CMPAZ,
                                     data.obs[iobs].header, &cmpaz);
        ierr += sacio_getFloatHeader(SAC_FLOAT_EVLA,
                                     data.obs[iobs].header, &evla);
        ierr += sacio_getFloatHeader(SAC_FLOAT_EVLO,
                                     data.obs[iobs].header, &evlo);
        ierr += sacio_getFloatHeader(SAC_FLOAT_STLA,
                                     data.obs[iobs].header, &stla);
        ierr += sacio_getFloatHeader(SAC_FLOAT_STLO,
                                     data.obs[iobs].header, &stlo);
        ierr += sacio_getFloatHeader(SAC_FLOAT_DELTA,
                                     data.obs[iobs].header, &dt0);
        ierr += sacio_getCharacterHeader(SAC_CHAR_KNETWK,
                                         data.obs[iobs].header, knetwk);
        ierr += sacio_getCharacterHeader(SAC_CHAR_KSTNM,
                                         data.obs[iobs].header, kstnm);
        ierr += sacio_getCharacterHeader(SAC_CHAR_KCMPNM,
                                         data.obs[iobs].header, kcmpnm);
        // this one isn't critical
        sacio_getFloatHeader(SAC_FLOAT_STEL,
                             data.obs[iobs].header, &stel);
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Error reading header variables\n", __func__);
            break;
        }
        // Station location code is not terribly important
        sacio_getCharacterHeader(SAC_CHAR_KHOLE, data.obs[iobs].header, khole);
        cmpincSEED = cmpinc - 90.0; // SAC to SEED convention
        // Get the primary arrival
        ierr = sacio_getEpochalStartTime(data.obs[iobs].header, &epoch);
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Error getting start time\n", __func__);
            break;
        }
        ierr += getPrimaryArrival(data.obs[iobs].header, &pickTime, phaseName);
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Error getting primary pick\n", __func__);
            break;
        }
        // Need to figure out the component
        icomp = 1;
        if (kcmpnm[2] == 'Z' || kcmpnm[2] == 'z' || kcmpnm[2] == '1')
        {
            icomp = 1;
        }
        else if (kcmpnm[2] == 'N' || kcmpnm[2] == 'n' || kcmpnm[2] == '2')
        {
            icomp = 2;
        }
        else if (kcmpnm[2] == 'E' || kcmpnm[2] == 'e' || kcmpnm[2] == '3')
        {
            icomp = 3;
        }
        else
        {
            fprintf(stderr, "%s: Can't classify component: %s\n",
                    __func__, kcmpnm);
        }
        // Process all Green's functions in this block
        for (id=0; id<ffGrns.ndepth; id++)
        {
            for (it=0; it<ffGrns.ntstar; it++)
            {
                idx = tdsearch_hudson_observationDepthTstarToIndex(iobs, id, it,
                                                                   ffGrns);
                kndx = 10*idx;
                sacio_getFloatHeader(SAC_FLOAT_O, ffGrns.grns[kndx].header, &o);
                getPrimaryArrival(ffGrns.grns[kndx].header,
                                  &pickTimeGrns, phaseNameGrns);
                if (strcasecmp(phaseNameGrns, phaseName) != 0)
                {
                    fprintf(stdout, "%s: Phase name mismatch %s %s\n",
                            __func__, phaseName, phaseNameGrns);
                }
                npts = ffGrns.grns[kndx].npts;
                indx = tdsearch_greens_getGreensFunctionIndex(G11_GRNS,
                                                              iobs, it, id,
                                                              *grns);
                for (i=0; i<6; i++)
                {
                    sacio_copy(ffGrns.grns[kndx], &grns->grns[indx+i]);
                    if (data.obs[iobs].pz.lhavePZ)
                    {
                        sacio_copyPolesAndZeros(data.obs[iobs].pz,
                                                &grns->grns[indx+i].pz);
                    }
                    sacio_setFloatHeader(SAC_FLOAT_AZ, az,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_BAZ, baz,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_CMPAZ, cmpaz,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_CMPINC, cmpinc,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_EVLA, evla,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_EVLO, evlo,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_STLA, stla,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_STLO, stlo,
                                         &grns->grns[indx+i].header);
                    sacio_setFloatHeader(SAC_FLOAT_STEL, stel,
                                         &grns->grns[indx+i].header);
                    sacio_setCharacterHeader(SAC_CHAR_KNETWK, knetwk,
                                             &grns->grns[indx+i].header);
                    sacio_setCharacterHeader(SAC_CHAR_KSTNM, kstnm,
                                             &grns->grns[indx+i].header);
                    sacio_setCharacterHeader(SAC_CHAR_KHOLE, khole,
                                             &grns->grns[indx+i].header);
                    sacio_setCharacterHeader(SAC_CHAR_KCMPNM, kcmpnms[i],
                                             &grns->grns[indx+i].header);
                    sacio_setCharacterHeader(SAC_CHAR_KEVNM, "SYNTHETIC\0",
                                             &grns->grns[indx+i].header);
                    // Set the start time by aligning on the arrival
                    epochNew = epoch + (pickTime - o) - pickTimeGrns;
                    sacio_setEpochalStartTime(epochNew,
                                              &grns->grns[indx+i].header);
                    // Update the pick times
                    for (l=0; l<11; l++)
                    {
                        ierr = sacio_getFloatHeader(pickVars[l],
                                                    grns->grns[indx+i].header,
                                                    &pick);
                        if (ierr == 0)
                        {
                            pick = pick + o;
                            sacio_setFloatHeader(pickVars[l],
                                                 pick,
                                                 &grns->grns[indx+i].header);
                        }
                    }
                }
                ierr = parmt_utils_ff2mtGreens64f(npts, icomp,
                                                  az, baz,
                                                  cmpaz, cmpincSEED,
                                                  ffGrns.grns[kndx+0].data,
                                                  ffGrns.grns[kndx+1].data,
                                                  ffGrns.grns[kndx+2].data,
                                                  ffGrns.grns[kndx+3].data,
                                                  ffGrns.grns[kndx+4].data,
                                                  ffGrns.grns[kndx+5].data,
                                                  ffGrns.grns[kndx+6].data,
                                                  ffGrns.grns[kndx+7].data,
                                                  ffGrns.grns[kndx+8].data,
                                                  ffGrns.grns[kndx+9].data,
                                                  grns->grns[indx+0].data,
                                                  grns->grns[indx+1].data,
                                                  grns->grns[indx+2].data,
                                                  grns->grns[indx+3].data,
                                                  grns->grns[indx+4].data,
                                                  grns->grns[indx+5].data);
                if (ierr != 0)
                {
                    fprintf(stderr, "%s: Failed to rotate Greens functions\n",
                            __func__);
                }
                // Fix the characteristic magnitude scaling in CPS
                for (i=0; i<6; i++)
                {
                    cblas_dscal(npts, xscal, grns->grns[indx+i].data, 1);
                }
            }
        }
    }
    return 0;
}
//============================================================================//
/*!
 * @brief Repicks the Green's functions onset time with an STA/LTA picker.
 *
 * @param[in] sta        Short term average window length (seconds).
 * @param[in] lta        Long term average window length (seconds).
 * @param[in] threshPct  Percentage of max STA/LTA after which an arrival
 *                       is declared.
 * @param[in] iobs       C numbered observation index.
 * @param[in] itstar     C numbered t* index.
 * @param[in] idepth     C numbered depth index.
 *
 * @param[in,out] grns   On input contains the Green's functions.
 *                       On output the first arrival time has been modified
 *                       with an STA/LTA picker.
 *
 * @brief 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_repickGreensWithSTALTA(
    const double sta, const double lta, const double threshPct,
    const int iobs, const int itstar, const int idepth,
    struct tdSearchGreens_struct *grns)
{
    struct stalta_struct stalta;
    double *charFn, *g, *Gxx, *Gyy, *Gzz, *Gxy, *Gxz, *Gyz,
           *gxxPad, *gyyPad, *gzzPad, *gxyPad, *gxzPad, *gyzPad,
           charMax, dt, tpick;
    int indices[6], ierr, k, npts, nlta, npad, nsta, nwork, prePad;
    // Check STA/LTA
    ierr = 0;
    memset(&stalta, 0, sizeof(struct stalta_struct));
    if (lta < sta || sta < 0.0)
    {
        if (lta < sta){fprintf(stderr,"%s: Error lta < sta\n", __func__);}
        if (sta < 0.0){fprintf(stderr,"%s: Error sta is negative\n", __func__);}
        return -1;
    }
    ierr = tdsearch_greens_getGreensFunctionsIndices(iobs, itstar, idepth,
                                                     *grns, indices);
    if (ierr != 0)
    {
        fprintf(stderr, "%s: Failed to get Greens functions indicies\n",
                 __func__);
        return -1;
    }
    ierr = sacio_getIntegerHeader(SAC_INT_NPTS,
                                  grns->grns[indices[0]].header, &npts);
    if (ierr != 0 || npts < 1)
    {
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Error getting number of points from header\n",
                    __func__);
        }
        else
        {
            fprintf(stderr, "%s: ERror no data points\n", __func__);
        }
        return -1;
    }
    ierr = sacio_getFloatHeader(SAC_FLOAT_DELTA,
                                grns->grns[indices[0]].header, &dt);
    if (ierr != 0 || dt <= 0.0)
    {
        if (ierr != 0){fprintf(stderr, "%s: failed to get dt\n", __func__);}
        if (dt <= 0.0){fprintf(stderr, "%s: invalid sampling period\n", __func__);}
        return -1;
    }
    // Define the windows
    nsta = (int) (sta/dt + 0.5);
    nlta = (int) (lta/dt + 0.5);
    prePad = MAX(64, fft_nextpow2(nlta, &ierr));
    npad = prePad + npts;
    // Set space
    gxxPad = memory_calloc64f(npad);
    gyyPad = memory_calloc64f(npad);
    gzzPad = memory_calloc64f(npad);
    gxyPad = memory_calloc64f(npad);
    gxzPad = memory_calloc64f(npad);
    gyzPad = memory_calloc64f(npad);
    charFn = memory_calloc64f(npad);
    // Reference pointers
    Gxx = grns->grns[indices[0]].data;
    Gyy = grns->grns[indices[1]].data;
    Gzz = grns->grns[indices[2]].data;
    Gxy = grns->grns[indices[3]].data;
    Gxz = grns->grns[indices[4]].data;
    Gyz = grns->grns[indices[5]].data;
    // Pre-pad signals
    array_set64f_work(prePad, Gxx[0], gxxPad);
    array_set64f_work(prePad, Gyy[0], gyyPad);
    array_set64f_work(prePad, Gzz[0], gzzPad);
    array_set64f_work(prePad, Gxy[0], gxyPad);
    array_set64f_work(prePad, Gxz[0], gxzPad);
    array_set64f_work(prePad, Gyz[0], gyzPad);
    // Copy rest of array
    array_copy64f_work(npts, Gxx, &gxxPad[prePad]);
    array_copy64f_work(npts, Gyy, &gyyPad[prePad]);
    array_copy64f_work(npts, Gzz, &gzzPad[prePad]);
    array_copy64f_work(npts, Gxy, &gxyPad[prePad]);
    array_copy64f_work(npts, Gxz, &gxzPad[prePad]);
    array_copy64f_work(npts, Gyz, &gyzPad[prePad]);
    // apply the sta/lta
    for (k=0; k<6; k++)
    {
        g = NULL;
        if (k == 0)
        {
            g = gxxPad;
        }
        else if (k == 1)
        {
            g = gyyPad;
        }
        else if (k == 2)
        {
            g = gzzPad;
        }
        else if (k == 3)
        {
            g = gxyPad;
        }
        else if (k == 4)
        {
            g = gxzPad;
        }
        else if (k == 5)
        {
            g = gyzPad;
        }
        ierr = stalta_setShortAndLongTermAverage(nsta, nlta, &stalta);
        if (ierr != 0)
        {
            printf("%s: Error setting STA/LTA\n", __func__);
            break;
        }
        ierr = stalta_setData64f(npad, g, &stalta);
        if (ierr != 0)
        {
            printf("%s: Error setting data\n", __func__);
            break;
        }
        ierr = stalta_applySTALTA(&stalta);
        if (ierr != 0)
        {
            printf("%s: Error applying STA/LTA\n", __func__);
            break;
        }
        ierr = stalta_getData64f(stalta, npad, &nwork, g);
        if (ierr != 0)
        {
            printf("%s: Error getting result\n", __func__);
            break;
        }
        cblas_daxpy(npad, 1.0, g, 1, charFn, 1);
        stalta_resetInitialConditions(&stalta);
        stalta_resetFinalConditions(&stalta);
        g = NULL;
    }
    // Compute the pick time
    charMax = array_max64f(npts, &charFn[prePad], &ierr);
    tpick =-1.0;
    for (k=prePad; k<npad; k++)
    {
        if (charFn[k] > 0.01*threshPct*charMax)
        {
            tpick = (double) (k - prePad)*dt;
            break;
        }
    }
    if (tpick ==-1.0)
    {
        tpick = (double) (array_argmax64f(npad, charFn, &ierr) - prePad)*dt;
    }
    // Overwrite the pick; by this point should be on SAC_FLOAT_A
    //double apick;
    //sacio_getFloatHeader(SAC_FLOAT_A, grns->grns[indices[0]].header, &apick);
    for (k=0; k<6; k++)
    {
        sacio_setFloatHeader(SAC_FLOAT_A, tpick,
                             &grns->grns[indices[k]].header);
    }
    // Dereference pointers and free space
    Gxx = NULL;
    Gyy = NULL;
    Gzz = NULL;
    Gxy = NULL;
    Gxz = NULL;
    Gyz = NULL;
    memory_free64f(&gxxPad);
    memory_free64f(&gyyPad);
    memory_free64f(&gzzPad);
    memory_free64f(&gxyPad);
    memory_free64f(&gxzPad);
    memory_free64f(&gyzPad);
    memory_free64f(&charFn);
    stalta_free(&stalta);
    return ierr;
}
//============================================================================//
/*!
 * @brief Modifies the Green's functions processing commands.
 *
 * @param[in] iodva     The output units for the hudson96 Green's functions.
 * @param[in] iodva     0 indicates the Green's functions have units
 *                      of displacement.
 * @param[in] iodva     1 indicates the Green's functions have units
 *                      of velocity.
 * @param[in] iodva     2 indicates the Green's functions have units
 *                      of acceleration.
 * @param[in] cut0      The cut time in seconds relative to the pick time
 *                      to begin the window around the arrival.
 * @parma[in] cut1      The cut time in seconds relative to the pick time
 *                      to end the window around the arrival.
 * @param[in] targetDt  Desired sampling period in seconds to which the Green's
 *                      functions should be resampled as to match the data.
 *
 * @param[in,out] grns  On input input contains the genreci Green's functions
 *                      processing commands.
 * @param[in,out] grns  On exit the Green's functions processing have been
 *                      made specific to the input Green's functions.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 */
int tdsearch_greens_modifyProcessingCommands(
    const int iodva,
    const double cut0, const double cut1, const double targetDt,
    struct tdSearchGreens_struct *grns)
{
    struct tdSearchModifyCommands_struct options;
    const char **cmds;
    char **newCmds;
    size_t lenos;
    int i, ierr, iobs, k, kndx1, kndx2, ncmds;
    ierr = 0;
    grns->cmdsGrns = (struct tdSearchDataProcessingCommands_struct *)
                     calloc((size_t) grns->ngrns,
                        sizeof(struct tdSearchDataProcessingCommands_struct));
    for (iobs=0; iobs<grns->nobs; iobs++)
    {
        newCmds = NULL;
        ncmds =  grns->cmds[iobs].ncmds;
        if (ncmds < 1){continue;} // Nothing to do
        cmds = (const char **) grns->cmds[iobs].cmds;
        options.cut0 = cut0;
        options.cut1 = cut1;
        options.targetDt = targetDt;
        options.ldeconvolution = false;
        options.iodva = iodva;
        kndx1 = iobs*(6*grns->ntstar*grns->ndepth);
        kndx2 = (iobs+1)*(6*grns->ntstar*grns->ndepth);
        newCmds = tdsearch_commands_modifyCommands(ncmds, (const char **) cmds,
                                                   options,
                                                   grns->grns[kndx1], &ierr);
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Failed to set processing commands\n", __func__);
            goto ERROR;
        }
        // Expand the processing commands
        for (k=kndx1; k<kndx2; k++)
        {
            grns->cmdsGrns[k].ncmds = ncmds;
            grns->cmdsGrns[k].cmds = (char **)
                                     calloc((size_t) ncmds, sizeof(char *));
            for (i=0; i<ncmds; i++)
            {
                lenos = strlen(newCmds[i]);
                grns->cmdsGrns[k].cmds[i] = (char *)
                                            calloc(lenos+1, sizeof(char));
                strcpy(grns->cmdsGrns[k].cmds[i], newCmds[i]);
            }
        }
        // Release the memory
        if (newCmds != NULL)
        {
            for (i=0; i<ncmds; i++)
            {
                if (newCmds[i] != NULL){free(newCmds[i]);}
            }
            free(newCmds);
        }
    }
ERROR:;
    return ierr;
}
//============================================================================//
/*!
 * @brief Processes the Green's functions.
 *
 * @param[in,out] grns    On input contains the Green's functions for all
 *                        observations, depths, and t*'s as well as the
 *                        processing chains.
 *                        On output contains the filtered Green's functions
 *                        for all observations, depths, and t*'s.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 * @author Ben Baker, ISTI
 *
 */
int tdsearch_greens_process(struct tdSearchGreens_struct *grns)
{
    double *data;
    struct serialCommands_struct commands;
    struct parallelCommands_struct parallelCommands;
    double dt, dt0, epoch, epoch0, time;
    int dataPtr[7], i, i0, ierr, iobs, idep, it, kndx, l, npts, npts0, nq,
        nwork, ny, ns;
    bool lnewDt, lnewStartTime;
    const int nsignals = 6;
    const int nTimeVars = 12;
    const enum sacHeader_enum timeVars[12]
       = {SAC_FLOAT_A, SAC_FLOAT_O,
          SAC_FLOAT_T0, SAC_FLOAT_T1, SAC_FLOAT_T2, SAC_FLOAT_T3,
          SAC_FLOAT_T4, SAC_FLOAT_T5, SAC_FLOAT_T6, SAC_FLOAT_T7,
          SAC_FLOAT_T8, SAC_FLOAT_T9};
    // Loop on the observations
    for (iobs=0; iobs<grns->nobs; iobs++)
    {
        for (idep=0; idep<grns->ndepth; idep++)
        {
            for (it=0; it<grns->ntstar; it++)
            {
                memset(&parallelCommands, 0,
                       sizeof(struct parallelCommands_struct));
                memset(&commands, 0, sizeof(struct serialCommands_struct));
                kndx = tdsearch_greens_getGreensFunctionIndex(G11_GRNS,
                                                              iobs, it, idep,
                                                              *grns);
                ierr = process_stringsToSerialCommandsOptions(
                                      grns->cmdsGrns[kndx].ncmds,
                                      (const char **) grns->cmdsGrns[kndx].cmds,
                                      &commands);
                if (ierr != 0)
                {
                    fprintf(stderr, "%s: Error setting serial command string\n", __func__);
                    goto ERROR;
                }
                // Determine some characteristics of the processing
                sacio_getEpochalStartTime(grns->grns[kndx].header, &epoch0);
                sacio_getFloatHeader(SAC_FLOAT_DELTA,
                                     grns->grns[kndx].header, &dt0);
                lnewDt = false;
                lnewStartTime = false;
                epoch = epoch0;
                dt = dt0;
                for (i=0; i<commands.ncmds; i++)
                {
                     if (commands.commands[i].type == CUT_COMMAND)
                     {
                         i0 = commands.commands[i].cut.i0;
                         epoch = epoch + (double) i0*dt;
                         lnewStartTime = true;
                     }
                     if (commands.commands[i].type == DOWNSAMPLE_COMMAND)
                     {
                         nq = commands.commands[i].downsample.nq;
                         dt = dt*(double) nq;
                         lnewDt = true;
                     }
                     if (commands.commands[i].type == DECIMATE_COMMAND)
                     {
                         nq = commands.commands[i].decimate.nqAll;
                         dt = dt*(double) nq;
                         lnewDt = true;
                     }
                }
                dataPtr[0] = 0;
                for (i=0; i<nsignals; i++)
                {
                    dataPtr[i+1] = dataPtr[i] + grns->grns[kndx+i].npts;
                }
                process_setCommandOnAllParallelCommands(nsignals, commands,
                                                        &parallelCommands);
                data = memory_calloc64f(dataPtr[nsignals]);
                for (i=0; i<nsignals; i++)
                {
                    ierr = array_copy64f_work(grns->grns[kndx+i].npts,
                                              grns->grns[kndx+i].data,
                                              &data[dataPtr[i]]);
                    if (ierr != 0)
                    {
                         fprintf(stderr, "%s: Failed to copy data\n", __func__);
                         goto ERROR;
                    }
                }
                ierr = process_setParallelCommandsData64f(nsignals, dataPtr,
                                                          data,
                                                          &parallelCommands);
                if (ierr != 0)
                {
                    fprintf(stderr, "%s: Failed to set data\n", __func__);
                    goto ERROR;
                }
                ierr = process_applyParallelCommands(&parallelCommands);
                if (ierr != 0)
                {
                    fprintf(stderr ,"%s: Failed to process data\n", __func__);
                    goto ERROR;
                }
                // Get the data
                nwork = dataPtr[nsignals];
                ierr = process_getParallelCommandsData64f(parallelCommands,
                                                          -1, nsignals,
                                                          &ny, &ns,
                                                          dataPtr, data);
                if (ny > nwork)
                {
                    memory_free64f(&data);
                    data = memory_calloc64f(ny);
                }
                nwork = ny;
                ierr = process_getParallelCommandsData64f(parallelCommands,
                                                          nwork, nsignals,
                                                          &ny, &ns,
                                                          dataPtr, data);
                for (i=0; i<nsignals; i++)
                {
                    sacio_getIntegerHeader(SAC_INT_NPTS,
                                           grns->grns[kndx+i].header, &npts0);
                    npts = dataPtr[i+1] - dataPtr[i];
                    // Resize event
                    if (npts != npts0)
                    {
                        sacio_freeData(&grns->grns[kndx+i]);
                        grns->grns[kndx+i].data = sacio_malloc64f(npts);
                        grns->grns[kndx+i].npts = npts;
                        sacio_setIntegerHeader(SAC_INT_NPTS, npts,
                                               &grns->grns[kndx+i].header);
                        ierr = array_copy64f_work(npts,
                                                  &data[dataPtr[i]],
                                                  grns->grns[kndx+i].data);
                    }
                    else
                    {
                        ierr = array_copy64f_work(npts,
                                                  &data[dataPtr[i]],
                                                  grns->grns[kndx+i].data);
                    }
                }
                // Update the times
                if (lnewStartTime)
                {
                    for (i=0; i<nsignals; i++)
                    {
                        // Update the picks
                        for (l=0; l<nTimeVars; l++)
                        {
                            ierr = sacio_getFloatHeader(timeVars[l],
                                              grns->grns[kndx+i].header, &time);
                            if (ierr == 0)
                            {
                                time = time + epoch0; // Turn to real time
                                time = time - epoch;  // Relative to new time
                                sacio_setFloatHeader(timeVars[l], time,
                                                    &grns->grns[kndx+i].header);
                            }
                        } // Loop on picks
                        sacio_setEpochalStartTime(epoch,
                                                  &grns->grns[kndx+i].header);
                    } // Loop on signals
                }
                // Update the sampling period
                if (lnewDt)
                {
                    for (i=0; i<nsignals; i++)
                    {
                        sacio_setFloatHeader(SAC_FLOAT_DELTA, dt,
                                             &grns->grns[kndx+i].header);
                    }
                }
                process_freeSerialCommands(&commands);
                process_freeParallelCommands(&parallelCommands);
                memory_free64f(&data);
//TODO fix this
tdsearch_greens_repickGreensWithSTALTA(2.0, 10.0, 80.0, iobs, it, idep, grns);
            }
        }
    }
ERROR:;
    return 0;
}
//============================================================================//
/*!
 * @brief Writes the Green's functions corresponding to the iobs'th observation
 *        for the given tstar, depth index.
 *
 * @param[in] dirnm    Directory name where the Green's functions should be
 *                     written.  If this is NULL then the Green's functions
 *                     will be written to the current working directory.
 * @param[in] iobs     Observation index in the range [0,nobs-1].
 * @param[in] itstar   The t* index in the range [0,ntstar-1].
 * @param[in] idepth   The depth index in the range [0,ndepth-1].
 * @param[in] grns     The structure containing the Green's functions.
 *
 * @result 0 indicates success.
 *
 * @ingroup tdsearch_greens
 *
 */
int tdsearch_greens_writeSelectGreensFunctions(
    const char *dirnm,
    const int iobs, const int itstar, const int idepth,
    const struct tdSearchGreens_struct grns)
{
    char fileName[PATH_MAX], rootName[PATH_MAX];
    size_t lenos;
    int i, ierr, indx;
    memset(rootName, 0, PATH_MAX*sizeof(char));
    if (dirnm == NULL)
    {
        strcpy(rootName, "./\0");
    }
    else
    {
        lenos = strlen(dirnm);
        if (lenos > 0)
        {
            strcpy(rootName, dirnm);
            if (rootName[lenos-1] != '/'){strcat(rootName, "/\0");}
        }
        else
        {
            strcpy(rootName, "./\0");
        }
    }
    if (!os_path_isdir(rootName))
    {
        ierr = os_makedirs(rootName);
        if (ierr != 0)
        {
            fprintf(stderr, "%s: Failed to make output directory %s\n",
                    __func__, rootName);
            return -1;
        }
    }
    // Get the indices to write
    indx =  tdsearch_greens_getGreensFunctionIndex(G11_GRNS,
                                                   iobs, itstar, idepth,
                                                   grns);
    if (indx < 0)
    {
        fprintf(stderr, "%s: Invalid index\n", __func__);
        return -1;
    }
    for (i=0; i<6; i++)
    {
        memset(fileName, 0, PATH_MAX*sizeof(char));
        sprintf(fileName, "%s%s.%s.%s.%s.DEPTH_%d.TSTAR_%d.SAC",
                rootName, grns.grns[indx+i].header.knetwk,
                grns.grns[indx+i].header.kstnm,
                grns.grns[indx+i].header.kcmpnm,
                grns.grns[indx+i].header.khole, idepth, itstar);
        sacio_writeTimeSeriesFile(fileName, grns.grns[indx+i]);
    }
    return 0;
}
//============================================================================//
static int getPrimaryArrival(const struct sacHeader_struct hdr,
                             double *time, char phaseName[8])
{
    const enum sacHeader_enum timeVars[11]
       = {SAC_FLOAT_A,
          SAC_FLOAT_T0, SAC_FLOAT_T1, SAC_FLOAT_T2, SAC_FLOAT_T3,
          SAC_FLOAT_T4, SAC_FLOAT_T5, SAC_FLOAT_T6, SAC_FLOAT_T7,
          SAC_FLOAT_T8, SAC_FLOAT_T9};
    const enum sacHeader_enum timeVarNames[11]
       = {SAC_CHAR_KA,
          SAC_CHAR_KT0, SAC_CHAR_KT1, SAC_CHAR_KT2, SAC_CHAR_KT3,
          SAC_CHAR_KT4, SAC_CHAR_KT5, SAC_CHAR_KT6, SAC_CHAR_KT7,
          SAC_CHAR_KT8, SAC_CHAR_KT9};
    int i, ifound1, ifound2;
    memset(phaseName, 0, 8*sizeof(char));
    for (i=0; i<11; i++)
    {
        ifound1 = sacio_getFloatHeader(timeVars[i], hdr, time);
        ifound2 = sacio_getCharacterHeader(timeVarNames[i], hdr, phaseName);
        if (ifound1 == 0 && ifound2 == 0){return 0;}
    }
    printf("%s: Failed to get primary pick\n", __func__);
    *time =-12345.0;
    memset(phaseName, 0, 8*sizeof(char));
    strcpy(phaseName, "-12345");
    return -1;
}