From cf7a5b72598944cf72e5de3f0bfbde48d9f0eae8 Mon Sep 17 00:00:00 2001
From: Kyle <kyle.c.klenk@gmail.com>
Date: Mon, 28 Nov 2022 22:05:30 +0000
Subject: [PATCH] added output container: it can be used to store data although
 it does not get the correct answer

---
 .../file_access_actor/file_access_actor.hpp   |  39 +-----
 .../file_access_actor/output_container.hpp    |  40 +++++-
 build/makefiles/makefile                      |   1 -
 build/makefiles/makefile_old_summa            |  12 +-
 .../cpp_code/file_access_actor.cpp            | 123 ++++++++++--------
 .../cpp_code/output_container.cpp             |  47 ++++++-
 .../containers/output_container/makefile      |  35 +++--
 .../containers/output_container/test.cpp      |  25 +++-
 8 files changed, 216 insertions(+), 106 deletions(-)

diff --git a/build/includes/file_access_actor/file_access_actor.hpp b/build/includes/file_access_actor/file_access_actor.hpp
index e8620f8..7e5b5e8 100644
--- a/build/includes/file_access_actor/file_access_actor.hpp
+++ b/build/includes/file_access_actor/file_access_actor.hpp
@@ -1,53 +1,26 @@
 #pragma once
 
-#include "caf/stateful_actor.hpp"
-#include "output_manager.hpp"
+#include "caf/actor.hpp"
 #include "forcing_file_info.hpp"
 #include "timing_info.hpp"
+#include "output_container.hpp"
 #include "settings_functions.hpp"
 #include "fortran_data_types.hpp"
 #include "auxilary.hpp"
 
 
-struct hru_output_handles {
-    // Statistic Structures
-    void* handle_forc_stat        = new_handle_var_dlength();
-    void* handle_prog_stat        = new_handle_var_dlength();
-    void* handle_diag_stat        = new_handle_var_dlength();
-    void* handle_flux_stat        = new_handle_var_dlength();
-    void* handle_indx_stat        = new_handle_var_dlength();
-    void* handle_bvar_stat        = new_handle_var_dlength();
-    // primary data structures (scalars)
-    void* handle_time_struct      = new_handle_var_i();
-    void* handle_forc_struct      = new_handle_var_d();
-    void* handle_attr_struct      = new_handle_var_d();
-    void* handle_type_struct      = new_handle_var_i();
-    void* handle_id_struct        = new_handle_var_i8();
-    // primary data structures (variable length vectors)
-    void* handle_indx_struct      = new_handle_var_ilength();
-    void* handle_mpar_struct      = new_handle_var_dlength();
-    void* handle_prog_struct      = new_handle_var_dlength();
-    void* handle_diag_struct      = new_handle_var_dlength();
-    void* handle_flux_struct      = new_handle_var_dlength();
-    // basin-average structures
-    void* handle_bpar_struct      = new_handle_var_d();
-    void* handle_bvar_struct      = new_handle_var_dlength();
-    // ancillary data structures
-    void* handle_dpar_struct      = new_handle_var_d();
-    void* handle_finalize_stats   = new_handle_var_i();
-    void* handle_output_timestep  = new_handle_var_i();
-
-};
+// class Output_Container;
 
 namespace caf {
 
+
 struct file_access_state {
     // Variables set on Spwan
     caf::actor parent; 
     int start_gru;
     int num_gru;
 
-    std::vector<hru_output_handles> vector_of_output_handles;
+    // std::vector<hru_output_handles> vector_of_output_handles;
 
     void *handle_forcing_file_info; // Handle for the forcing file information
     void *handle_ncid;              // output file ids
@@ -58,6 +31,8 @@ struct file_access_state {
     int filesLoaded;
     int err;
 
+    Output_Container *output_container;
+
     File_Access_Actor_Settings file_access_actor_settings;
 
     std::vector<Forcing_File_Info> forcing_file_list; // list of steps in file
diff --git a/build/includes/file_access_actor/output_container.hpp b/build/includes/file_access_actor/output_container.hpp
index df83426..9c40bbd 100644
--- a/build/includes/file_access_actor/output_container.hpp
+++ b/build/includes/file_access_actor/output_container.hpp
@@ -1,6 +1,40 @@
 #pragma once
 
-#include "file_access_actor.hpp"
+#include "fortran_data_types.hpp"
+#include <vector>
+#include <iostream>
+
+
+struct hru_output_handles {
+    // Statistic Structures
+    void* handle_forc_stat        = new_handle_var_dlength();
+    void* handle_prog_stat        = new_handle_var_dlength();
+    void* handle_diag_stat        = new_handle_var_dlength();
+    void* handle_flux_stat        = new_handle_var_dlength();
+    void* handle_indx_stat        = new_handle_var_dlength();
+    void* handle_bvar_stat        = new_handle_var_dlength();
+    // primary data structures (scalars)
+    void* handle_time_struct      = new_handle_var_i();
+    void* handle_forc_struct      = new_handle_var_d();
+    void* handle_attr_struct      = new_handle_var_d();
+    void* handle_type_struct      = new_handle_var_i();
+    void* handle_id_struct        = new_handle_var_i8();
+    // primary data structures (variable length vectors)
+    void* handle_indx_struct      = new_handle_var_ilength();
+    void* handle_mpar_struct      = new_handle_var_dlength();
+    void* handle_prog_struct      = new_handle_var_dlength();
+    void* handle_diag_struct      = new_handle_var_dlength();
+    void* handle_flux_struct      = new_handle_var_dlength();
+    // basin-average structures
+    void* handle_bpar_struct      = new_handle_var_d();
+    void* handle_bvar_struct      = new_handle_var_dlength();
+    // ancillary data structures
+    void* handle_dpar_struct      = new_handle_var_d();
+    void* handle_finalize_stats   = new_handle_var_i();
+    void* handle_output_timestep  = new_handle_var_i();
+
+};
+
 // This class holds the output for the HRUs as a buffer so 
 // we can write more data at once
 class Output_Container {
@@ -18,8 +52,10 @@ class Output_Container {
         // insertes output from an HRU into hru_output_handles
         void insertOutput(int hru_index, hru_output_handles hru_output);
 
+        bool isFull(int hru_index);
+
         // returns the matrix of hru_outputs for writing
-        std::vector<std::vector<hru_output_handles>> getAllHRUOuptut();
+        std::vector<std::vector<hru_output_handles>> getAllHRUOutput();
 
 
 };
\ No newline at end of file
diff --git a/build/makefiles/makefile b/build/makefiles/makefile
index c471e04..299cfbe 100644
--- a/build/makefiles/makefile
+++ b/build/makefiles/makefile
@@ -26,7 +26,6 @@ FLAGS_ACTORS = -g -O3 -Wfatal-errors -std=c++17
 # FLAGS_ACTORS = -g -O0 -Wall -std=c++17
 
 
-
 #========================================================================
 # PART 1: Define directory paths
 #========================================================================
diff --git a/build/makefiles/makefile_old_summa b/build/makefiles/makefile_old_summa
index aab38d0..25c8f25 100644
--- a/build/makefiles/makefile_old_summa
+++ b/build/makefiles/makefile_old_summa
@@ -20,10 +20,10 @@ FLAGS_SUMMA = -O3 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors
 FLAGS_ACTORS = -O3 -Wfatal-errors -std=c++17
 
 # Debug runs
-# FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fbacktrace -Wno-unused -Wno-unused-dummy-argument -fPIC
-# FLAGS_COMM = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC
-# FLAGS_SUMMA = -g -O0 -Wall -ffree-line-length-none -fmax-errors=0 -fbacktrace -fcheck=bounds -fPIC
-# FLAGS_ACTORS = -g -O0 -Wall -std=c++17
+FLAGS_NOAH = -g -O0 -ffree-form -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors
+FLAGS_COMM = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors
+FLAGS_SUMMA = -g -O0 -ffree-line-length-none -fmax-errors=0 -fPIC -Wfatal-errors
+FLAGS_ACTORS = -g -O0 -Wfatal-errors -std=c++17
 
 
 
@@ -309,7 +309,7 @@ GRUinfo = $(SOURCE_DIR)/job_actor/GRUinfo.cpp
 FILE_ACCESS_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/file_access_actor
 FILE_ACCESS_ACTOR = $(SOURCE_DIR)/file_access_actor/cpp_code/file_access_actor.cpp
 FORCING_FILE_INFO = $(SOURCE_DIR)/file_access_actor/cpp_code/forcing_file_info.cpp
-OUTPUT_MANAGER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_manager.cpp
+OUTPUT_CONTAINER = $(SOURCE_DIR)/file_access_actor/cpp_code/output_container.cpp
 
 HRU_ACTOR_INCLUDES = -I$(INCLUDE_DIR)/hru_actor
 HRU_ACTOR = $(SOURCE_DIR)/hru_actor/cpp_code/hru_actor.cpp
@@ -374,7 +374,7 @@ compile_hru_actor:
 	$(CC) $(FLAGS_ACTORS) -c $(HRU_ACTOR) $(HRU_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES)
 
 compile_file_access_actor:
-	$(CC) $(FLAGS_ACTORS) -c $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) $(OUTPUT_MANAGER) \
+	$(CC) $(FLAGS_ACTORS) -c $(OUTPUT_CONTAINER) $(FILE_ACCESS_ACTOR) $(FORCING_FILE_INFO) \
 	$(FILE_ACCESS_ACTOR_INCLUDES) $(GLOBAL_INCLUDES) $(ACTORS_INCLUDES) $(SUMMA_ACTOR_INCLUDES) 
 
 compile_job_actor:
diff --git a/build/source/actors/file_access_actor/cpp_code/file_access_actor.cpp b/build/source/actors/file_access_actor/cpp_code/file_access_actor.cpp
index 369cc53..c3def21 100644
--- a/build/source/actors/file_access_actor/cpp_code/file_access_actor.cpp
+++ b/build/source/actors/file_access_actor/cpp_code/file_access_actor.cpp
@@ -31,6 +31,9 @@ behavior file_access_actor(stateful_actor<file_access_state>* self, int start_gr
     self->state.handle_ncid = new_handle_var_i();
     self->state.err = 0;
 
+    int max_steps = 120;
+    self->state.output_container = new Output_Container(num_gru, max_steps);
+
 
         
     initalizeFileAccessActor(self);
@@ -172,70 +175,88 @@ behavior file_access_actor(stateful_actor<file_access_state>* self, int start_gr
              - If no do nothing
             */
 
-
-
-
-            initalizeOutputHandles(self);
+            // initalizeOutputHandles(self);
+            hru_output_handles hru_output;
             
             int err = 0;
             // statistic structures
-            set_var_dlength(forc_stat, self->state.output_handles.handle_forc_stat);
-            set_var_dlength(prog_stat, self->state.output_handles.handle_prog_stat);
-            set_var_dlength(diag_stat, self->state.output_handles.handle_diag_stat);
-            set_var_dlength(flux_stat, self->state.output_handles.handle_flux_stat);
-            set_var_dlength(indx_stat, self->state.output_handles.handle_indx_stat);
-            set_var_dlength(bvar_stat, self->state.output_handles.handle_bvar_stat);
+            set_var_dlength(forc_stat, hru_output.handle_forc_stat);
+            set_var_dlength(prog_stat, hru_output.handle_prog_stat);
+            set_var_dlength(diag_stat, hru_output.handle_diag_stat);
+            set_var_dlength(flux_stat, hru_output.handle_flux_stat);
+            set_var_dlength(indx_stat, hru_output.handle_indx_stat);
+            set_var_dlength(bvar_stat, hru_output.handle_bvar_stat);
             // primary data structures (scalars)
-            set_var_i(time_struct, self->state.output_handles.handle_time_struct);
-            set_var_d(forc_struct, self->state.output_handles.handle_forc_struct);
-            set_var_d(attr_struct, self->state.output_handles.handle_attr_struct);
-            set_var_i(type_struct, self->state.output_handles.handle_type_struct);
-            set_var_i8(id_struct, self->state.output_handles.handle_id_struct);
+            set_var_i(time_struct, hru_output.handle_time_struct);
+            set_var_d(forc_struct, hru_output.handle_forc_struct);
+            set_var_d(attr_struct, hru_output.handle_attr_struct);
+            set_var_i(type_struct, hru_output.handle_type_struct);
+            set_var_i8(id_struct,  hru_output.handle_id_struct);
             // primary data structures (variable length vectors)
-            set_var_ilength(indx_struct, self->state.output_handles.handle_indx_struct);
-            set_var_dlength(mpar_struct, self->state.output_handles.handle_mpar_struct);
-            set_var_dlength(prog_struct, self->state.output_handles.handle_prog_struct);
-            set_var_dlength(diag_struct, self->state.output_handles.handle_diag_struct);
-            set_var_dlength(flux_struct, self->state.output_handles.handle_flux_struct);
+            set_var_ilength(indx_struct, hru_output.handle_indx_struct);
+            set_var_dlength(mpar_struct, hru_output.handle_mpar_struct);
+            set_var_dlength(prog_struct, hru_output.handle_prog_struct);
+            set_var_dlength(diag_struct, hru_output.handle_diag_struct);
+            set_var_dlength(flux_struct, hru_output.handle_flux_struct);
             // basin-average structures
-            set_var_d(bpar_struct, self->state.output_handles.handle_bpar_struct);
-            set_var_dlength(bvar_struct, self->state.output_handles.handle_bvar_struct);
+            set_var_d(bpar_struct, hru_output.handle_bpar_struct);
+            set_var_dlength(bvar_struct, hru_output.handle_bvar_struct);
             // ancillary data structures
-            set_var_d(dpar_struct, self->state.output_handles.handle_dpar_struct);
-            set_var_i(finalize_stats, self->state.output_handles.handle_finalize_stats);
-            set_var_i(output_timestep, self->state.output_handles.handle_output_timestep);
+            set_var_d(dpar_struct, hru_output.handle_dpar_struct);
+            set_var_i(finalize_stats, hru_output.handle_finalize_stats);
+            set_var_i(output_timestep, hru_output.handle_output_timestep);
+
+            // self->state.vector_of_output_handles.push_back(self->state.output_handles);
+
+            self->state.output_container->insertOutput(index_gru, hru_output);
+
+            aout(self) << "Is container full: " << self->state.output_container->isFull(index_gru) << "\n";
+
+            if (self->state.output_container->isFull(index_gru)) {
+                aout(self) << "Writing output for GRU: " << index_gru << "\n";
+
+                std::vector<std::vector<hru_output_handles>> hru_output = self->state.output_container->getAllHRUOutput();
+
+                for (int i = 0; i < hru_output[0].size(); i++) {
+
+                writeBasinToNetCDF(self->state.handle_ncid, &index_gru,
+                    hru_output[0][i].handle_finalize_stats, 
+                    hru_output[0][i].handle_output_timestep, 
+                    hru_output[0][i].handle_bvar_stat,
+                    hru_output[0][i].handle_bvar_struct, &err);
+
+                writeTimeToNetCDF(self->state.handle_ncid,
+                    hru_output[0][i].handle_finalize_stats, 
+                    hru_output[0][i].handle_output_timestep, 
+                    hru_output[0][i].handle_time_struct, &err);
+
+                writeDataToNetCDF(self->state.handle_ncid, &index_gru, &index_hru,
+                    hru_output[0][i].handle_finalize_stats, 
+                    hru_output[0][i].handle_forc_stat, 
+                    hru_output[0][i].handle_forc_struct,
+                    hru_output[0][i].handle_prog_stat, 
+                    hru_output[0][i].handle_prog_struct, 
+                    hru_output[0][i].handle_diag_stat, 
+                    hru_output[0][i].handle_diag_struct, 
+                    hru_output[0][i].handle_flux_stat, 
+                    hru_output[0][i].handle_flux_struct,
+                    hru_output[0][i].handle_indx_stat, 
+                    hru_output[0][i].handle_indx_struct, 
+                    hru_output[0][i].handle_output_timestep,
+                    &err);
+                }
+                
+            }
 
-            self->state.vector_of_output_handles.push_back(self->state.output_handles);
 
-            // writeBasinToNetCDF(self->state.handle_ncid, &index_gru,
-            //     self->state.output_handles.handle_finalize_stats, 
-            //     self->state.output_handles.handle_output_timestep, 
-            //     self->state.output_handles.handle_bvar_stat,
-            //     self->state.output_handles.handle_bvar_struct, &err);
         
-            // writeTimeToNetCDF(self->state.handle_ncid,
-            //     self->state.output_handles.handle_finalize_stats, 
-            //     self->state.output_handles.handle_output_timestep, 
-            //     self->state.output_handles.handle_time_struct, &err);
-
-            // writeDataToNetCDF(self->state.handle_ncid, &index_gru, &index_hru,
-            //     self->state.output_handles.handle_finalize_stats, 
-            //     self->state.output_handles.handle_forc_stat, 
-            //     self->state.output_handles.handle_forc_struct,
-            //     self->state.output_handles.handle_prog_stat, 
-            //     self->state.output_handles.handle_prog_struct, 
-            //     self->state.output_handles.handle_diag_stat, 
-            //     self->state.output_handles.handle_diag_struct, 
-            //     self->state.output_handles.handle_flux_stat, 
-            //     self->state.output_handles.handle_flux_struct,
-            //     self->state.output_handles.handle_indx_stat, 
-            //     self->state.output_handles.handle_indx_struct, 
-            //     self->state.output_handles.handle_output_timestep,
-            //     &err);
+
+
+
             
             self->state.file_access_timing.updateEndPoint("write_duration");
 
-            deallocateOutputHandles(self);
+            // deallocateOutputHandles(self);
         
         },
 
diff --git a/build/source/actors/file_access_actor/cpp_code/output_container.cpp b/build/source/actors/file_access_actor/cpp_code/output_container.cpp
index 360bfdc..f447bfe 100644
--- a/build/source/actors/file_access_actor/cpp_code/output_container.cpp
+++ b/build/source/actors/file_access_actor/cpp_code/output_container.cpp
@@ -4,11 +4,52 @@
 Output_Container::Output_Container(int max_hrus, int max_steps) {
     this->max_hrus = max_hrus;
     this->max_steps = max_steps;
+    for (int i = 0; i < max_hrus; i++) {
+        std::vector<hru_output_handles> hru_output_handles;
+        this->hru_output_handles_vector.push_back(hru_output_handles);
+    }
     
 }
 
 Output_Container::~Output_Container(){};
 
-void insertOutput(int hru_index, hru_output_handles hru_output) {
-    
-};
\ No newline at end of file
+void Output_Container::insertOutput(int hru_index, hru_output_handles hru_output) {
+    // adjust hru_index to be 0 based
+    hru_index = hru_index - 1;
+    try {
+        if (hru_index < 0 || hru_index >= this->max_hrus)
+        throw "HRU index out of bounds";
+            
+        if (this->hru_output_handles_vector[hru_index].size() < this->max_steps)
+            this->hru_output_handles_vector[hru_index].push_back(hru_output);
+        else
+            throw "HRU output buffer full";
+
+    } catch (const char* msg) {
+        std::cerr << msg << std::endl;
+    }
+}
+
+
+bool Output_Container::isFull(int hru_index) {
+    // adjust hru_index to be 0 based
+    hru_index = hru_index - 1;
+    try {
+        if (hru_index < 0 || hru_index >= this->max_hrus)
+        throw "HRU index out of bounds";
+            
+        if (this->hru_output_handles_vector[hru_index].size() == this->max_steps)
+            return true;
+        else
+            return false;
+
+    } catch (const char* msg) {
+        std::cerr << msg << std::endl;
+    }
+    return false;
+}
+
+std::vector<std::vector<hru_output_handles>> Output_Container::getAllHRUOutput() {
+    return this->hru_output_handles_vector;
+}
+
diff --git a/build/source/testing/containers/output_container/makefile b/build/source/testing/containers/output_container/makefile
index b947342..075fc3e 100644
--- a/build/source/testing/containers/output_container/makefile
+++ b/build/source/testing/containers/output_container/makefile
@@ -1,24 +1,39 @@
+FC = gfortran
 CC = g++	  # C++
-INCLUDES= -I$(EBROOTCAF)/include
-LIBRARIES= -L$(EBROOTCAF)/lib -lcaf_core -lcaf_io
+INCLUDES=-I/usr/local/include 
+LIBRARIES=-L/usr/local/lib -L/Summa-Actors/build/source/testing/containers/output_container -lcaf_core -lcaf_io -lcppwrap_datatypes
 FLAGS = -g -O0 -Wfatal-errors -std=c++17
 
-SOURCE_DIR = /home/kklenk/Summa-Projects/Summa-Actors/build/source/actors/file_access_actor/cpp_code
 
-CONTAINER_INCLUDES = -I/home/kklenk/Summa-Projects/Summa-Actors/build/includes/file_access_actor
+Fortran_Data_Types = /Summa-Actors/build/source/actors/global/cppwrap_datatypes.f90
+DATA_TYPES = /Summa-Actors/build/source/dshare/data_types.f90
+NR_TYPE = /Summa-Actors/build/source/engine/nrtype.f90
+VAR_LOOKUP = /Summa-Actors/build/source/dshare/var_lookup.f90
 
-GLOBAL_INCLUDES = -I/home/kklenk/Summa-Projects/Summa-Actors/build/includes/global
+SOURCE_DIR = /Summa-Actors/build/source/actors/file_access_actor/cpp_code
+
+CONTAINER_INCLUDES = -I/Summa-Actors/build/includes/file_access_actor
+GLOBAL_INCLUDES = -I/Summa-Actors/build/includes/global
 
 Output_Container = $(SOURCE_DIR)/output_container.cpp
 
 
+all: compile_fortran link_fortran clean_fortran compile_cpp link_cpp clean_cpp
+
+compile_fortran:
+	$(FC) -c $(NR_TYPE) $(VAR_LOOKUP) $(DATA_TYPES) $(Fortran_Data_Types) 
+
+link_fortran:
+	$(FC) -shared *.o -o libcppwrap_datatypes.so
+
+clean_fortran:
+	rm -f *.o *.mod 
 
-all: compile link clean
 
-compile:
+compile_cpp:
 	$(CC) $(FLAGS) -c main.cpp test.cpp $(Output_Container) $(CONTAINER_INCLUDES) $(GLOBAL_INCLUDES)
-link:
-	$(CC) $(FLAGS) -Wl,-rpath='$(EBROOTCAF)/lib' -o main *.o $(LIBRARIES)
+link_cpp:
+	$(CC) $(FLAGS) -Wl,-rpath='/usr/local/lib:/Summa-Actors/build/source/testing/containers/output_container' -o main *.o $(LIBRARIES)
 
-clean:
+clean_cpp:
 	rm *.o
\ No newline at end of file
diff --git a/build/source/testing/containers/output_container/test.cpp b/build/source/testing/containers/output_container/test.cpp
index 0be4a58..fd888c9 100644
--- a/build/source/testing/containers/output_container/test.cpp
+++ b/build/source/testing/containers/output_container/test.cpp
@@ -5,6 +5,29 @@ void TEST_CLIENT() {
     std::cout << "Testing Output Container\n";
 
     // Call constructor
-    Output_Container output_container = Output_Container(3,4);
+    int num_hrus = 2;
+    int num_steps = 2;
+    Output_Container output_container = Output_Container(num_hrus, num_steps);
+
+    // Test insertOutput
+    hru_output_handles hru_output0;
+    hru_output_handles hru_output1;
+    hru_output_handles hru_output2;
+    hru_output_handles hru_output3;
+
+
+    output_container.insertOutput(1, hru_output2);
+    output_container.insertOutput(1, hru_output3);
+    output_container.insertOutput(2, hru_output0);
+    output_container.insertOutput(2, hru_output3);
+    std::cout << "\nTesting for out of bounds errors - for 0\n";
+    output_container.insertOutput(0, hru_output0);
+    output_container.insertOutput(0, hru_output1);
+    std::cout << "\nTesting for out of bounds errors - for above max_hru\n";
+    output_container.insertOutput(5, hru_output2);
+    std::cout << "\nTesting for buffer full errors\n";
+    output_container.insertOutput(1, hru_output3);
+    
+
 
 }
\ No newline at end of file
-- 
GitLab