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

   This file is part of SnnsCLib, a fork of the kernel and parts of the gui of 
   the Stuttgart Neural Network Simulator (SNNS), version 4.3.

   The file's original version is part of SNNS 4.3. It's source code can be found at

   http://www.ra.cs.uni-tuebingen.de/SNNS/

   SNNS 4.3 is under the license LGPL v2. We note that source code files of SNNS 4.3 
   state as version "4.2". Base of this fork is SNNS 4.3 with a reverse-applied 
   python patch (see http://developer.berlios.de/projects/snns-dev/).

   SnnsCLib was developed in 2010 by Christoph Bergmeir under supervision of 
   José M. Benítez, both affiliated to DiCITS Lab, Sci2s group, DECSAI, 
   University of Granada

   Changes done to the original code were performed with the objective to
   port it from C to C++ and to encapsulate all code in one class named SnnsCLib.

   Changes in header files mainly include:
   * removed all static keywords
   * moved initializations of variables to the constructor of SnnsCLib

   Changes in cpp code files mainly include:
   * changed file ending from .c to .cpp
   * removed all SNNS internal includes and only include SnnsCLib   
   * static variables within functions were turned into member variables of SnnsCLib
   * function declarations were changed to method declarations, i.e. "SnnsCLib::.."
     was added
   * calls to the function table are now "C++-style", using the "this"-pointer

   License of SnnsCLib:
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
 
   This library 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
   Library General Public License for more details.
 
   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.

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


/*****************************************************************************
  FILE           : $Source: /projects/higgs1/SNNS/CVS/SNNS/kernel/sources/kr_JordElm.c,v $
  SHORTNAME      : kr_JordElm.c
  SNNS VERSION   : 4.2

  PURPOSE        : Topological sorting for Jordan and Elman networks 
  NOTES          :

  AUTHOR         : Tobias Soyez
  DATE           : 09.11.1993

  CHANGED BY     : 
  RCS VERSION    : $Revision: 2.8 $
  LAST CHANGE    : $Date: 1998/03/03 14:08:32 $

    Copyright (c) 1990-1995  SNNS Group, IPVR, Univ. Stuttgart, FRG
    Copyright (c) 1996-1998  SNNS Group, WSI, Univ. Tuebingen, FRG
******************************************************************************/

#include "SnnsCLib.h"

/*****************************************************************************
  FUNCTION : kr_recTouchContextUnits

  PURPOSE  : touches only context units recursively
  NOTES    : 

  RETURNS  : 
  UPDATE   : 
******************************************************************************/

void SnnsCLib::kr_recTouchContextUnits (struct Unit *unit_ptr)

{
  struct Site   *site_ptr ;
  struct Link   *link_ptr ;
  bool           unit_has_incoming_links = FALSE;

  if (unit_ptr->flags & UFLAG_REFRESH) return ;

  if ((IS_HIDDEN_UNIT (unit_ptr)) && (IS_SPECIAL_UNIT (unit_ptr)))
  {
    /* -----------  touch only context units ------------- */

    unit_ptr->flags |= UFLAG_REFRESH ;             /* set the 'touch' flag  */

    unit_has_incoming_links = FALSE ;

    switch (unit_ptr->flags & UFLAG_INPUT_PAT)
    {
      case  UFLAG_DLINKS:                          /* unit has direct links  */
        FOR_ALL_LINKS (unit_ptr, link_ptr)
	{ 
          kr_recTouchContextUnits (link_ptr->to) ; 
          unit_has_incoming_links = TRUE ;
        }
        break ;

      case  UFLAG_SITES:	                         /*  unit has sites  */
        FOR_ALL_SITES_AND_LINKS (unit_ptr, site_ptr, link_ptr)
	{
	  kr_recTouchContextUnits (link_ptr->to) ; 
          unit_has_incoming_links = TRUE ;
        }
        break ;
    }
  }

  if ((! unit_has_incoming_links) && (! IS_INPUT_UNIT (unit_ptr)))
  {
    /* unit has no incoming links -> dead unit -> delete touch flag */

    unit_ptr->flags &= ~UFLAG_REFRESH ;
  }
}



/*****************************************************************************
  FUNCTION : kr_recTopoCheckJE

  PURPOSE  : recursive topology check, called by kr_topoCheckJE
  NOTES    : 

  RETURNS  : 
  UPDATE   : 
******************************************************************************/

void  SnnsCLib::kr_recTopoCheckJE (struct Unit *unit_ptr, int depth)

{
  struct Site   *site_ptr ;
  struct Link   *link_ptr ;
  bool           unit_has_incoming_links ;



  if ((IS_HIDDEN_UNIT (unit_ptr)) && (IS_SPECIAL_UNIT (unit_ptr))) 
  {
    /* --------  touch context units recursively  -------- */
 
    kr_recTouchContextUnits (unit_ptr) ;
    return ;
  }

  if (unit_ptr->flags & UFLAG_REFRESH)
  {  
    if (unit_ptr->lln == 0)
    {  
      /*  logical layer no. isn't set => Cycle found  */
      topo_msg.no_of_cycles++ ;
      
      if (topo_msg.error_code == KRERR_NO_ERROR)
      { 
        topo_msg.src_error_unit = unit_ptr - unit_array ;
        topo_msg.error_code     = KRERR_CYCLES ;
      }
    }
    return ;
  }

 
  /* -----  unit is not a context unit and has not been touched yet  ------ */
  /* -----  continue recursive depth search                          ------ */

  unit_ptr->flags |= UFLAG_REFRESH ;             /* set the 'touch' flag  */

  unit_has_incoming_links = FALSE ;

  switch (unit_ptr->flags & UFLAG_INPUT_PAT)
  {
    case  UFLAG_DLINKS:                          /* unit has direct links  */
      FOR_ALL_LINKS (unit_ptr, link_ptr)
      {
	kr_recTopoCheckJE (link_ptr->to, depth + 1) ; 
        unit_has_incoming_links = TRUE ;
      }
      break ;

    case  UFLAG_SITES:	                         /*  unit has sites  */
      FOR_ALL_SITES_AND_LINKS (unit_ptr, site_ptr, link_ptr)
      {
	kr_recTopoCheckJE (link_ptr->to, depth + 1) ; 
        unit_has_incoming_links = TRUE ;
      }
      break ;
  }

  /*  remember the depth (for cycle detection and statistics)  */
  unit_ptr->lln = depth ;

  /*  store highest layer no.  */
  if (depth > topo_msg.no_of_layers) topo_msg.no_of_layers = depth ;


  if ((! unit_has_incoming_links) && (! IS_INPUT_UNIT (unit_ptr)))
  {
    /* unit has no incoming links -> dead unit -> delete touch flag */

    unit_ptr->flags &= ~UFLAG_REFRESH ;
  }
}



/*****************************************************************************
  FUNCTION : kr_topoCheckJE

  PURPOSE  : Checks the topology of partial recurrent networks 
             (i.e. JORDAN or ELMAN networks) :
	     only recurrent links to context units are allowed
  NOTES    : 

  RETURNS  : kernel error code
  UPDATE   : 
******************************************************************************/

krui_err SnnsCLib::kr_topoCheckJE (void)

{
  struct Unit  *unit_ptr ;
  bool          o_units  ;


  topo_msg.no_of_cycles     = 
  topo_msg.no_of_dead_units =
  topo_msg.dest_error_unit  = 
  topo_msg.src_error_unit   =
  topo_msg.no_of_layers     = 0 ;
  topo_msg.error_code       = KernelErrorCode = KRERR_NO_ERROR ;


  if (NoOfUnits == 0)
  {
    /*  no units defined  */
    KernelErrorCode = KRERR_NO_UNITS ;
    return (KernelErrorCode) ;
  }


  /* -------------------  reset units 'touch' flags  ----------------------- */

  FOR_ALL_UNITS (unit_ptr)
    if (UNIT_IN_USE (unit_ptr))
    {
      unit_ptr->flags &= ~UFLAG_REFRESH ;
      unit_ptr->lln = 0 ;
    }


  /* ----------  begin depth search at the first output unit  -------------- */

  o_units = FALSE ;
  FOR_ALL_UNITS (unit_ptr)
    if (IS_OUTPUT_UNIT (unit_ptr) && UNIT_IN_USE (unit_ptr))
    {
      o_units = TRUE ;
      kr_recTopoCheckJE (unit_ptr, 1) ;
      if (topo_msg.error_code != KRERR_NO_ERROR)
      {  /*  stop if an error occured  */
        KernelErrorCode = topo_msg.error_code ;
        return (KernelErrorCode) ;
      }
    }
      
  if (!o_units)
  {  /*  no output units */
    KernelErrorCode = KRERR_NO_OUTPUT_UNITS ;
    return (KernelErrorCode) ;
  }


  /* ----------  search for dead units i.e. units without inputs  ---------- */

  FOR_ALL_UNITS (unit_ptr)
    if (!(unit_ptr->flags & UFLAG_REFRESH) && UNIT_IN_USE (unit_ptr))
    {
      topo_msg.error_code = KRERR_DEAD_UNITS ;
      topo_msg.no_of_dead_units++ ;
      if (topo_msg.src_error_unit == 0)
        topo_msg.src_error_unit = unit_ptr - unit_array ;
    }

  if (topo_msg.no_of_dead_units != 0)
    KernelErrorCode = KRERR_DEAD_UNITS ;

  return (topo_msg.error_code) ;
}



/*****************************************************************************
  FUNCTION : kr_recTopoSortJE

  PURPOSE  : stores only hidden (NOT special hidden !) units in the topologic 
             array
  NOTES    : 

  RETURNS  : 
  UPDATE   : 
******************************************************************************/

void  SnnsCLib::kr_recTopoSortJE (struct Unit *unit_ptr, int depth)

{
  struct Site   *site_ptr ;
  struct Link   *link_ptr ;


  /* ---------------------  ignore context units  ------------------------- */

  if ((IS_HIDDEN_UNIT (unit_ptr)) && (IS_SPECIAL_UNIT (unit_ptr)))
  {
    unit_ptr->flags |= UFLAG_REFRESH ;    /* set the 'touch' flag */
    return ;
  } 


  if (unit_ptr->flags & UFLAG_REFRESH)
  {  
    /* the 'touch' flag is set: don't continue search */
    topo_msg.src_error_unit = unit_ptr - unit_array ;  /* store unit number */

    if IS_OUTPUT_UNIT (unit_ptr)
    {  
      /*  this output unit has a output connection to another unit  */
      if (topo_msg.error_code == KRERR_NO_ERROR)
        topo_msg.error_code = KRERR_O_UNITS_CONNECT ;
    }
    else
      if (unit_ptr->lln == 0)
      {  
        /*  logical layer no. isn't set => Cycle found  */
          topo_msg.no_of_cycles++ ;
        if (topo_msg.error_code == KRERR_NO_ERROR)
          topo_msg.error_code = KRERR_CYCLES ;
      }

    return ;
  }
  else
    unit_ptr->flags |= UFLAG_REFRESH ; /* set the 'touch' flag */


  /* -------------------------  continue search  -------------------------- */

  switch (unit_ptr->flags & UFLAG_INPUT_PAT)
  {
    case  UFLAG_DLINKS:   
      FOR_ALL_LINKS (unit_ptr, link_ptr)
	kr_recTopoSortJE (link_ptr->to, depth + 1) ; 
      break ;

    case  UFLAG_SITES:	
      FOR_ALL_SITES_AND_LINKS (unit_ptr, site_ptr, link_ptr)
	kr_recTopoSortJE (link_ptr->to, depth + 1) ; 
      break ;
  }

  /*  remember the depth (for cycle detection and statistics)  */
  unit_ptr->lln = depth ; 


  /* ------------------------  store hidden units  ------------------------ */

  if IS_HIDDEN_UNIT( unit_ptr )
  {
    *topo_ptr++ = unit_ptr ; 
    no_of_topo_units++ ;
  }
}



/*****************************************************************************
  FUNCTION : kr_topoSortJE

  PURPOSE  : sorts units of a partial recurrent network by their topologic 
             type input, hidden, output, context and stores the pointers to 
             this units in the topologic array
  NOTES    : ###########  V E R Y    I M P O R T A N T   N O T E  ############
             no_of_topo_units contains only the number of input, hidden and
             output units 

             no_of_topo_units DOES NOT contain the no. of context units !!!

             all special hidden units (io-type = SPECIAL_H) are assumed to
             be context units 

             !! before calling this function:                  !!
             !! check the network topology with kr_topoCheckJE !!

  RETURNS  : error code 
  UPDATE   : 
******************************************************************************/

krui_err SnnsCLib::kr_topoSortJE (void)

{ 
  register struct Unit	 *unit_ptr ;


  KernelErrorCode  = KRERR_NO_ERROR ;  /* reset return code                  */
  topo_ptr         = topo_ptr_array ;  /* initialize global pointer          */
  NoOfInputUnits   = 0 ;
  NoOfOutputUnits  = 0 ;
  no_of_topo_units = 0 ;
  *topo_ptr++      = NULL ;            /* limit left side of the topological */
                                       /* array with NULL pointer            */


  /* ------------------  reset 'touch' flags of all units  ----------------- */

  FOR_ALL_UNITS (unit_ptr)
    if (UNIT_IN_USE (unit_ptr))
    {
      unit_ptr->flags &= ~UFLAG_REFRESH ;
      unit_ptr->lln = 0 ;
    }


  /* -----------  store all input units in the topologic array  ------------ */

  FOR_ALL_UNITS (unit_ptr)
    if (IS_INPUT_UNIT (unit_ptr) && UNIT_IN_USE (unit_ptr))
    {
      if UNIT_HAS_INPUTS (unit_ptr)
      { 
        /* links to input units are not allowed */ 
        topo_msg.dest_error_unit = unit_ptr - unit_array ;  
	KernelErrorCode          = KRERR_I_UNITS_CONNECT ; 
        return (KernelErrorCode) ;
      }

      NoOfInputUnits++   ;	
      no_of_topo_units++ ;	
      *topo_ptr++ = unit_ptr ;  
    }

  *topo_ptr++ = NULL ;  /* limit input units in the topological array with   */
                        /* NULL pointer                                      */

  if (NoOfInputUnits == 0)
  {  
    KernelErrorCode = KRERR_NO_INPUT_UNITS ;
    return( KernelErrorCode ) ;
  }


  /* -----------  store all hidden units in the topologic array  ----------- */
  /* sorts hidden units by their topological order using recursive depth     */
  /* search starting at the first output unit                                */

  FOR_ALL_UNITS( unit_ptr )
    if (IS_OUTPUT_UNIT (unit_ptr) && UNIT_IN_USE (unit_ptr))
    {
      kr_recTopoSortJE (unit_ptr, 1) ;  
      if (topo_msg.error_code != KRERR_NO_ERROR)
      {  
        KernelErrorCode = topo_msg.error_code ;
        return (KernelErrorCode) ;
      }
    }

  *topo_ptr++ = NULL ;  /* limit hidden units in the topological array with  */
                        /* NULL pointer                                      */


  /* -----------  store all output units in the topologic array  ----------- */

  FOR_ALL_UNITS (unit_ptr)
    if (IS_OUTPUT_UNIT (unit_ptr) && UNIT_IN_USE (unit_ptr))
    {
      NoOfOutputUnits++  ;	
      no_of_topo_units++ ;
      *topo_ptr++ = unit_ptr ;
    }

  if (NoOfOutputUnits == 0)
  { 
    KernelErrorCode = KRERR_NO_OUTPUT_UNITS ;
    return (KernelErrorCode) ;
  }

  *topo_ptr++ = NULL ;  /* limit output units in the topological array with  */
                        /* NULL pointer                                      */


  /* ----------  store all context units in the topologic array  ----------- */
  /*       !!!  no_of_topo_units MUST NOT BE INCREMENTED ANY MORE  !!!       */
  
  FOR_ALL_UNITS (unit_ptr)
    if (IS_HIDDEN_UNIT (unit_ptr) && IS_SPECIAL_UNIT (unit_ptr) &&
        UNIT_IN_USE (unit_ptr))
    {
      *topo_ptr++ = unit_ptr ;
    }

  *topo_ptr++ = NULL ;  /* limit context units in the topological array with */
                        /* NULL pointer                                      */


  /* ---------  search for dead units i.e. units without inputs  ----------- */

  FOR_ALL_UNITS (unit_ptr)
    if (!(unit_ptr->flags & UFLAG_REFRESH) && UNIT_IN_USE (unit_ptr))
    {
      topo_msg.no_of_dead_units++ ;
      if (topo_msg.src_error_unit == 0)
        topo_msg.src_error_unit = unit_ptr - unit_array ; 
    }

  if (topo_msg.no_of_dead_units != 0) KernelErrorCode = KRERR_DEAD_UNITS ;


  return( KernelErrorCode ) ;
}



/*****************************************************************************
                        E N D     O F     F I L E
******************************************************************************/




