//# Slicer.h: specify which elements to extract from an n-dimensional array
//# Copyright (C) 1994,1995,1997,1999
//# Associated Universities, Inc. Washington DC, USA.
//#
//# 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; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//# Internet email: aips2-request@nrao.edu.
//# Postal address: AIPS++ Project Office
//# National Radio Astronomy Observatory
//# 520 Edgemont Road
//# Charlottesville, VA 22903-2475 USA
//#
//# $Id$
#ifndef CASA_SLICER_2_H
#define CASA_SLICER_2_H
//# Includes
#include "IPosition.h"
namespace casacore { //# NAMESPACE CASACORE - BEGIN
//# Forward Declarations
class Slice;
//
// Specify which elements to extract from an n-dimensional array
//
//
// The review and modification of this class were undertaken, in part,
// with the aim of making this class header an example -- this is what
// the Casacore project thinks a class header should look like.
//
//
// You should have at least a preliminary understanding of these classes:
// IPosition
// Array
// Slice
//
//
// The class name "Slicer" may be thought of as a short form
// of "n-Dimensional Slice Specifier." Some confusion is possible
// between class "Slice" and this class.
//
//
//
// If you need to extract or operate upon a portion of an array,
// the Slicer class is the best way to specify the subarray you are
// interested in.
//
// Slicer has many constructors. Of these, some require that the
// programmer supply a full specification of the array elements he
// wants to extract; other constructors make do with partial information.
// In the latter case, the constructor will assume sensible default values or,
// when directed, infer missing information from the array that's getting
// sliced (hereafter, the "source" array).
//
// Constructing With Full Information
//
// To fully specify a subarray, you must supply three pieces of information
// for each axis of the subarray:
//
//
// - where to start
//
- how many elements to extract
//
- what stride (or "increment" or "interval") to use: a stride of
// "n" means pick extract only every "nth" element along an axis
//
//
// The most basic constructor for Slicer illustrates this. To create
// an Slicer for getting selected elements from a 3D array:
//
//
// IPosition start (3,0,0,0), length (3,10,10,10), stride (3,3,3,3);
// Slicer slicer (start, length, stride);
// // assume proper declarations, and meaningful values in the source array
// subArray = sourceArray (slicer);
//
// It gets elements 0,3,6,9,12,15,18,21,24,27 for each dimension.
//
// If you wish to extract elements from the array
// at intervals, these intervals must be regular. The interval is one
// constant integer for each dimension of the array: it cannot be a function.
//
//
// "length", the second parameter to the Slicer
// constructor above, may actually be used in two ways. In normal
// (and default) use, it specifies how many elements to select from the
// source. In the alternative use, it specifies the index of the last element
// to extract from the source array. This ambiguity (does "end" mean
// "length" or does it mean "last index"?) is handled by a default
// fourth parameter to the constructor. This code fragment will
// extract the same subarray as the example above:
//
// IPosition start (3,0,0,0), end (3,27,27,27), stride (3,3,3,3);
// Slicer slicer (start, end, stride, Slicer::endIsLast);
// subArray = sourceArray (slicer);
//
// Note that in this example end(3,28,29,28) gives the same result.
// (We use "end" as the name of the formal parameter because it supports
// both meanings -- "last index" or "length." You may wish to use a
// clarifying name for the actual parameter in your code, as we have
// above when we used "length".)
//
// Similar to Python it is possible to address the start and/or end value
// from the end by giving a negative value (-1 means the last value).
// However, a length and stride cannot be negative.
// Unlike Python the end value is inclusive (as discussed above).
// For example,
//
// Slicer slicer (IPosition(1,-4), IPosition(1,-2), Slicer::endIsLast)
// Slicer slicer (IPosition(1,6), IPosition(1,8), Slicer::endIsLast)
//
// Both Slicers give the same result when used on a Vector with length 10.
//
// Constructing with Partial Information
//
// Some of the constructors don't require complete information: Slicer
// either calculates sensible default values or deduces them from the
// source array. If you do not specify a "stride" argument, for example,
// a value of 1 will be used for all dimensions. If you specify a "start"
// but nothing else, a stride of 1, and (perhaps against expectation)
// a length of 1 will be used.
//
// Note that using a negative start or end is also partial information.
// The actual array shape is needed to derive the exact start or end value.
//
// To instruct the Slicer to get otherwise unspecified information
// from the source array, you can create an IPosition like "end"
// as shown here:
//
//
// IPosition start (3,0,0,0), stride (3,3,3,3);
// IPosition end (3,Slicer::MimicSource, Slicer::MimicSource,
// Slicer::MimicSource);
// Slicer smartSlicer (start, end, stride);
// // assume proper declarations...
// subArray = sourceArray (smartSlicer)
//
//
// If you are a library programmer, and write a class that can be sliced
// by the Slicer class, you need to understand the mechanism for
// completing the information which the application programmer, in using
// your class, specified incompletely. (If you are an application
// programmer, who wants to slice a library class, this explanation will
// be only of academic interest.)
//
// When the source array (the library class you provide) gets the Slicer --
// which typically comes when the source array is asked to return a
// reference to a subarray -- the source does a callback to the Slicer
// object. The source array passes its own shape as one of the arguments
// to the Slicer callback and asks the Slicer to fill in the missing
// values from that shape.
//
// In use, and with an imagined class "MyVector", code would look
// like this:
//
// // first, a fragment from the application program:
// IPosition start (1,10), end (1, Slicer::MimicSource);
// Slicer slicer (start, end);
// MyVector v0 (100);
// MyVector v1 = v0 (slicer);
// //....
// // second, a fragment from a constructor of the library class "MyVector":
// // the MyVector class will construct v1 as a reference to
// // selected elements of v0, using (among other things) a
// // callback to the slicer it was passed (above, in the
// // construction of v1.
// //
// IPosition start, end, stride;
// fullSliceInformation =
// slicer.inferShapeFromSource (MyVector::shape(), start, end, stride);
// // now the MyVector instance knows everything it needs to
// // construct the instance.
//
// Please note that v1 will have a length of 90, and refer to elements
// 10-99 of v0.
//
// An exception will be thrown if the positions
// defined in the Slicer exceed the source array's shape.
//
//
//
//
// Given a large image, 4k on a side, extract (by sampling) an image
// 1k on a side, but covering the same region as the original.
//
//
// Image image ("N5364.fits"); // a 4-d VLA map, 4096 x 4096 x 3 x 1
// IPosition start (4,0,0,0,0), stride (4,4,4,1,1);
// IPosition end (4, Slicer::MimicSource, Slicer::MimicSource,
// Slicer::MimicSource, Slicer::MimicSource);
// Slicer smartSlicer (start, end, stride);
// // assume proper declarations...
// Image subImage = image (smartSlicer);
//
//
//
//
// Slicer is particularly convenient for designers of other library
// classes: Array and Image, for example. (In fact, this convenience
// was the original motivation for the class.) The benefit
// is this: the application programmer, who needs a slice of an Array,
// may provide slicing specifications in many different ways, but the
// Array class author needs to provide only one member function to
// return the slice. The Slicer class, in effect, and with its
// many constructors, provides a way to funnel all of the variety
// into a single member function call to the array or image class.
//
// For example, imagine a 100 x 100 x 100 array from which you want to
// extract various subarrays. Here are some of the ways you might
// specify the the subarray in the -absence- of Slicer.
//
//
// // preliminaries: create a cube and assign values to all elements --
// // this will be "source" array
// Cube bigCube (IPosition (3, 100, 100, 100));
// assignValues (bigCube);
// // declare a smaller cube, the destination array.
// Cube smallCube (IPosition (3, 10, 10, 10));
//
// // example 1: use Slice objects to extract a subcube -- the first
// // ten elements along each axis
// Slice xIndices (0,10,1), yIndices (0,10,1), zIndices (0,10,1);
// smallCube = bigCube (xIndices, yIndices, zIndices);
//
// // example 2: get the same subcube using three IPosition arguments
// IPosition start (3,0,0,0), end (3,10,10,10), stride (3,1,1,1);
// smallCube = bigCube (start, end, stride);
//
// // example 3: use 2 IPositions, letting the 3rd (stride) default to
// // IPosition (3,1,1,1)
// smallCube = bigCube (start, end);
//
//
// So the Cube class (together with its base class) must define three separate
// member functions for the essentially identical operation of
// extracting a subcube. The same replication is also required of
// Image, Array, and the other Array subclasses (Matrix and Vector).
//
// The Slicer class collapses all of this into a single member
// function per class:
//
//
// Slicer slicer = (call the constructor that best suits your problem)
// smallCube = bigCube (slicer);
//
//
// Since there are many constructors available for Slicer, you
// can still specify the subarray that you may want in a number of
// different ways, by constructing the Slicer in the way most natural
// to your circumstances. You then pass the Slicer to the array, and
// you will get back the slice you want.
//
// This class also offers the application programmer considerable
// flexibility by allowing the shape of the source array to determine
// some of the slice specification. This benefit is explained and
// demonstrated above.
//
//
// This class, and the TableArray, Array and Image classes,
// could allow for the extraction of a subarray with fewer axes than the
// source array. At present, for example, you cannot, directly slice
// a matrix from a cube.
//
class Slicer
{
public:
// Define the "MimicSource" value which defines the open start or end.
// This value should be different from MIN_INT in IPosition.h.
// It should also not be the lowest possible value, since that
// will probably be used as an undefined value.
// It must be a negative number.
enum {MimicSource= -2147483646};
// Define the possible interpretations of the end-value.
enum LengthOrLast {
// The end-values given in the constructor define the lengths.
endIsLength,
// The end-values given in the constructor define the trc.
endIsLast
};
// Construct a 1-dimensional Slicer.
// Start and end are inferred from the source; stride=1.
// "endIsLength" and "endIsLast" are identical here, so there's
// no need to discriminate between them by using a default parameter.
Slicer();
// The member function inferShapeFromSource
// (invoked as a callback by the
// source array) will use the shape of the source array for the
// unspecified values: IPosition elements with the value
// Slicer::MimicSource
//
// ArraySlicerError
//
// Create a Slicer with a given start, end (or length), and stride.
// An exception will be thrown if a negative length or non-positive
// stride is given or if the IPositions start, end, and stride
// do not have the same dimensionality.
// If length or stride is not given, they default to 1.
//
It is possible to leave values in start and end undefined
// by giving the value MimicSource. They can be filled
// in later with the actual array shape using function
// inferShapeFromSource.
//
Slicer (const IPosition& start, const IPosition& end,
const IPosition& stride,
LengthOrLast endInterpretation = endIsLength);
Slicer (const IPosition& start, const IPosition& end,
LengthOrLast endInterpretation = endIsLength);
explicit Slicer (const IPosition& start);
//
// Create a Slicer object from Slice objects.
// In a Slice object one defines the start, length, and stride for
// one axis.
// The default Slice constructor (called with no arguments) creates
// a Slice with start and length equal to zero, and an undefined stride.
//
// Create a Slicer for a 1-dimensional array.
Slicer (const Slice& x, LengthOrLast endInterpretation = endIsLength);
// Create a Slicer for a 2-dim array.
Slicer (const Slice& x, const Slice& y,
LengthOrLast endInterpretation = endIsLength);
// Create a Slicer for a 3-dim array.
Slicer (const Slice& x, const Slice& y, const Slice& z,
LengthOrLast endInterpretation = endIsLength);
//
// Equality
bool operator==(const Slicer&) const;
// Return the number of dimensions of the Slicer.
size_t ndim() const;
// This function checks all of the start, length (or end),
// and stride IPositions, and fills in missing values by
// getting the corresponding values from the shape of the
// source array.
// These will first be resized, if necessary.
// If, for a given axis, (end < start) , it means that a
// length of zero was specified.
// An exception is thrown if the
// start, end, or length exceeds the array shape or if the
// dimensionality of the array and Slicer do not conform.
//
// ArraySlicerError
//
IPosition inferShapeFromSource
(const IPosition& shape, IPosition& startResult,
IPosition& endResult, IPosition& strideResult) const;
// Report the defined starting position.
const IPosition& start() const;
// Report the defined ending position.
const IPosition& end() const;
// Report the defined stride.
const IPosition& stride() const;
// Report the length of the resulting axes.
const IPosition& length() const;
// Are all values fixed (i.e., no MimicSource given)?
bool isFixed() const;
// Set the start and end positions. No explicit checking is done that
// the input parameters make sense, so you must be certain if you
// call these. These are useful if you have a loop with many iterations
// and you do not wish the overhead of creating a new Slicer object
// for each iteration if the only thing you are doing is adjusting
// the start and end positions. Other than for performance reasons,
// these methods should not be called and you should prefer the
// error checking provided by constructing a new Slicer object.
// Note that the length is not updated, so in principle care should
// be taken that the length does not change.
//
void setStart (const IPosition& start)
{ start_p = start; }
void setEnd (const IPosition& end)
{ end_p = end; }
//
private:
LengthOrLast asEnd_p;
IPosition start_p;
IPosition end_p;
IPosition stride_p;
IPosition len_p; // Length of input
bool fixed_p; // no MimicSource used
// Define a private constructor taking an ssize_t.
// This is to prevent the user from the unexpected and meaningless
// Slicer that would result when the ssize_t argument is promoted to
// an IPosition.
// Slicer (ssize_t);
// Check the given start, end/length and stride.
// Fill in the length or end.
// It also calls fillFixed to fill the fixed flag.
void fillEndLen();
// Fill in start, len and stride from a Slice.
void fillSlice (const Slice&, ssize_t& start, ssize_t& length,
ssize_t& stride);
// Fill the fixed flag.
void fillFixed();
};
// IO functions for Slicer's
//
// Print the contents of the specified Slicer to the specified stream.
std::ostream& operator << (std::ostream& stream, const Slicer& slicer);
//
std::string to_string(const Slicer& slicer);
inline size_t Slicer::ndim() const
{ return start_p.nelements(); }
inline const IPosition& Slicer::start() const
{ return start_p; }
inline const IPosition& Slicer::end() const
{ return end_p; }
inline const IPosition& Slicer::stride() const
{ return stride_p; }
inline const IPosition& Slicer::length() const
{ return len_p; }
inline bool Slicer::isFixed() const
{ return fixed_p; }
} //# NAMESPACE CASACORE - END
#endif