/*
**
* BEGIN_COPYRIGHT
*
* Copyright (C) 2008-2018 SciDB, Inc.
* All Rights Reserved.
*
* SciDB is free software: you can redistribute it and/or modify
* it under the terms of the AFFERO GNU General Public License as published by
* the Free Software Foundation.
*
* SciDB is distributed "AS-IS" AND WITHOUT ANY WARRANTY OF ANY KIND,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE. See
* the AFFERO GNU General Public License for the complete license terms.
*
* You should have received a copy of the AFFERO GNU General Public License
* along with SciDB.  If not, see <http://www.gnu.org/licenses/agpl-3.0.html>
*
* END_COPYRIGHT
*/

/****************************************************************************/
/**
 *  @file       prelude.txt
 *
 *  @brief      Defines 'built-in' macros that ship with SciDB.
 *
 *  @details    This text file defines a suite of macros that are 'built into'
 *              SciDB and that are automatically loaded into every coordinator
 *              instance whenever it starts up or restarts anew.
 *
 *              Engine code generally assumes that these macros loaded and are
 *              defined as below,  so it is bad idea to edit, hide, rename, or
 *              change in any other way the meaning of these entities.
 */
/****************************************************************************/

/**
 * Return the number of non empty cells in the _ARRAY_ array. A simple alias
 * for the deprecated count() aggregate.
 *
 *   eg. op_count ( list('instances') );
 */
op_count ( _ARRAY_ ) = aggregate ( _ARRAY_, count(*));

/**
 * Return the sum of values in the _ATTR_ attribute of the _ARRAY_ array. A
 * simple alias for the deprecated sum() aggregate.
 *
 *   eg. op_sum ( list('instances'), instance_id );
 */
op_sum ( _ARRAY_, _ATTR_ ) = aggregate ( _ARRAY_, sum( _ATTR_ ));

/**
 * Return the arithmetic mean of values in the _ATTR_ attribute of the
 * _ARRAY_ array. A simple alias for the deprecated avg() aggregate.
 *
 *   eg. op_avg ( list('instances'), instance_id );
 */
op_avg ( _ARRAY_, _ATTR_ ) = aggregate ( _ARRAY_, avg( _ATTR_ ));

/**
 * Return the stdev of values in the _ATTR_ attribute of the
 * _ARRAY_ array. A simple alias for the deprecated stdev() aggregate.
 *
 *   eg. op_stdev ( list('instances'), instance_id );
 */
op_stdev ( _ARRAY_, _ATTR_ ) = aggregate ( _ARRAY_, stdev( _ATTR_ ));

/**
 * Return the smallest value in the _ATTR_ attribute of the _ARRAY_ array. A
 * simple alias for the now deprecated min() aggregate.
 *
 *   eg. op_min ( list('instances'), name );
 */
op_min ( _ARRAY_, _ATTR_ ) = aggregate ( _ARRAY_, min( _ATTR_ ));

/**
 * Return the greatest value in the _ATTR_ attribute of the _ARRAY_ array. A
 * simple alias for the now deprecated max() aggregate.
 *
 *   eg. op_max ( list('instances'), name );
 */
op_max ( _ARRAY_, _ATTR_ ) = aggregate ( _ARRAY_, max( _ATTR_ ));

/**
 * Returns smallest and largest values in the _ATTR_ attribute of the
 * _ARRAY_ array, and the range ... the ( max - min ) value. That is, the
 * difference between these max and min values.
 *
 *   eg. op_range(apply(list('instances'),DT,datetime(online_since)),DT)
 */
op_range ( _ARRAY_, _ATTR_ ) = apply (
  aggregate ( _ARRAY_,
             min( _ATTR_ ) AS min,
             max( _ATTR_ ) AS max
            ) AS _OP_RANGE_AGGR_,
  range, _OP_RANGE_AGGR_.max - _OP_RANGE_AGGR_.min
);

/**
 * Returns the precise count of the number of distinct values in the _ATTR_
 * attribute of the _ARRAY_ array.
 *
 *   eg. op_distinct_count ( list('instances'), instance_id );
 */
op_distinct_count ( _ARRAY_, _ATTR_ ) = op_count (
  uniq ( sort ( project ( _ARRAY_, _ATTR_ ), _ATTR_ ) )
);

/**
 *  Returns a 1D array with 1 cell and a single datetime attribute containing
 *  the current, local date time and timezone offset. This macro is
 *  useful for timing queries using an interactive tool like iquery.
 *
 *   eg.  op_now();
 *
 */
op_now() = build(<when : datetimetz>[R=0:0,1,0], tznow());

/**
 *  Macro functions that return the larger, or the smaller, of their two
 *  inputs.
 *
 *   eg. project(apply(list('instances'),MX,func_max(port,instance_id)),MX);
 */
func_max ( _FIRST_, _SECOND_ ) = iif ( _FIRST_ > _SECOND_, _FIRST_, _SECOND_ );
func_min ( _FIRST_, _SECOND_ ) = iif ( _FIRST_ < _SECOND_, _FIRST_, _SECOND_ );

/**
 *  Macro to pull out the current version and code revision of SciDB.
 */
op_scidbversion() = redimension(
  apply(
    project(
      filter(list('libraries'), name='SciDB' and inst=0), 
      major, minor, patch
    ), 
    i, inst
  ), 
  <major:uint32, minor:uint32, patch:uint32>[i=0:0,1,0]
);

/**
 *  Macros to set the values of a particular attribute in a named array at
 *  the specified array coordinates.
 *
 *  NOTE: Undocumented. Included here as an example of what's possible.
 */
op_set_cell_attr_1D( _ARRAY_, _DIM_NAME_, _DIM_COORD_VALUE_, _ATTR_NAME_, _ATTR_VALUE_ ) = insert(
  redimension(
    apply(
      build( < _randomDummyXXXXX_ : bool > [ dummy13841random=0:0,1,0], true ),
      _DIM_NAME_, int64(_DIM_COORD_VALUE_),
      _ATTR_NAME_, _ATTR_VALUE_ ),
    _ARRAY_
  ),
  _ARRAY_
);

op_set_cell_attr_2D( _ARRAY_, _DIM_NAME_1_, _DIM_COORD_VALUE_1_, _DIM_NAME_2_, _DIM_COORD_VALUE_2_,  _ATTR_NAME_, _ATTR_VALUE_ ) = insert(
  redimension(
    apply(
      build( < _randomDummyXXXXX_ : bool > [ dummy13841random=0:0,1,0], true ),
      _DIM_NAME_1_, int64(_DIM_COORD_VALUE_1_),
      _DIM_NAME_2_, int64(_DIM_COORD_VALUE_2_),
      _ATTR_NAME_, _ATTR_VALUE_ ),
    _ARRAY_
  ),
  _ARRAY_
);

/****************************************************************************/
/*
 *  The following macros are implemented in the desugager (because they depend
 *  upon supprted macro features such as variadic arguments), but are stubbed
 *  here so that they nevertheless appear in "list('macros')".
 */
/****************************************************************************/

load(output_array,input_file,instance_id,format,max_errors,shadow_array,isStrict) = 
  store(
    input(output_array,input_file,instance_id,format,max_errors,shadow_array,isStrict),
    output_array);

/****************************************************************************/
/*
 *  The following macro pushes data to to particular instances.  Only the
 *  'all' and instance_id parameters are documented.  The others are included
 *  as they are experimentally used in some internal use cases.
 */
/****************************************************************************/

_func_distrib( _DISTRIB_ ) =
     iif (_DISTRIB_='all', 0,
   	 iif (_DISTRIB_='round-robin', 1,
	      iif (_DISTRIB_='rows', 3,
	           iif (_DISTRIB_='columns', 4, 2))));

_func_inst( _DISTRIB_ ) =
     iif (_DISTRIB_='all', '-1',
  	 iif (_DISTRIB_='round-robin', '-1',
	      iif (_DISTRIB_='rows', '-1',
 	           iif (_DISTRIB_='columns', '-1', _DISTRIB_))));

redistribute( _ARRAY_, _DISTRIB_ ) =
      _sg(_ARRAY_, _func_distrib(string(_DISTRIB_)), int64(_func_inst(string(_DISTRIB_))));


/****************************************************************************/
/*
 *  The following macros are undocumented, and are strictly for use within the
 *  engine: certain translations emitted by the compiler rely upon them.
 */
/****************************************************************************/

/**
 * Return the cell_instance_imblance of _ARRAY_
 * where cell_instance_imbalance is defined as
 * max-cells-per-instance / expected-cells-per-instance
 *
 * NOTE: _ prefix indicates it is undocumented/unsupported
 */
_cell_instance_imbalance( _ARRAY_STR_ ) =
    apply(apply(cross_join(aggregate(aggregate(project(filter(cross_join(filter(list('arrays'),name=_ARRAY_STR_) as A,
                                                                         filter(list('chunk map'), attid=0) as C),
                                                              A.uaid = C.uaid),
                                                       nelem),
                                               sum(nelem) as necells, inst),
                                               max(necells),
                                               sum(necells)),
                           aggregate(list('instances'),count(instance_id) as ninstances)),
                 necells_ideal,
                 int64(necells_sum/double(ninstances))),
           imbalance, double(necells_max) / double(necells_ideal));

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