/* -*- mode: C++ -*- */
//////////////////////////////////////////////////////////////////////////////
// FILE   : MatQuery.tol
// PURPOSE: Defines package MatQuery
//////////////////////////////////////////////////////////////////////////////

NameBlock MatQuery = [[

Text _.autodoc.description = "Functions and classes related with efficient "
"selection and classification methods over large matricial data.";
Text _.autodoc.url = "";
Set _.autodoc.keys = [["select","classify"]];
Set _.autodoc.authors = [[
   "vdebuen@tol-project.org"   ]];

//It will be passed to built-in function Pack to convert to the best storage
//Default value is set to unknown that will ensure minimum space
Real _.packSparsity = ?;

//////////////////////////////////////////////////////////////////////////////
Real _checkColumnVector(Text method, VMatrix M)
//////////////////////////////////////////////////////////////////////////////
{
  If(VColumns(M)==1,True,
  { 
    WriteLn("[MatQuery::"+method+"] a column matrix was "
    "expected instead of ("<<VRows(M)+"x"<<VColumns(M)+")","E");
    False
  }) 
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SelectMatch = 
"Returns the rows where thecolumn matrix 'match' has non zero value";
Set SelectMatch(VMatrix match)
//////////////////////////////////////////////////////////////////////////////
{
  If(!_checkColumnVector("SelectMatch",match),Copy(Empty), {
  Matrix select = VMat2Triplet(Convert(match,"Cholmod.R.Triplet"));
  If(!Rows(select),Copy(Empty), MatSet(Tra(SubCol(select,[[1]])))[1])
})};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.MatchRowsFullKnown = 
"Returns a column matrix with True in rows where all cells have konwn values";
VMatrix MatchRowsFullKnown(VMatrix M)
//////////////////////////////////////////////////////////////////////////////
{
  Real m = VRows(M);
  Real n = VColumns(M);
  Pack(Not(IsUnknown(M)*Rand(n,1,1,1)),_.packSparsity)
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.MatchRowsWithValue = 
"Returns a column matrix with True in rows where given column matrix M has "
"exactly the specified value, that might even be the unknown value";
VMatrix MatchRowsWithValue(VMatrix M, Real value)
//////////////////////////////////////////////////////////////////////////////
{
  If(!_checkColumnVector("MatchRowsWithValue",M), Rand(0,0,0,0), {
  Pack(If(IsUnknown(value), IsUnknown(M),
  {
    Real r = VRows(M);
    VMatrix V = Rand(r,1,value,value);
    EQ(M,V)
  }),_.packSparsity)
})};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SelectRowsWithValue = 
"Selects rows of a column matrix whose cell is exactly the specified value";
Set SelectRowsWithValue(VMatrix M, Real value)
//////////////////////////////////////////////////////////////////////////////
{
  SelectMatch(MatchRowsWithValue(M,value))   
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.MatchRowsInInterval = 
"Returns a column matrix with True in rows where given column matrix M is "
"inside the interval defined as: \n"
"  [min,max]: if minIncluded=True  and minIncluded=True\n"
"  (min,max]: if minIncluded=False and minIncluded=True\n"
"  [min,max): if minIncluded=True  and minIncluded=False\n"
"  (min,max): if minIncluded=False and minIncluded=False\n";
VMatrix MatchRowsInInterval(
  VMatrix M, 
  Real minIncluded,
  Real min, 
  Real max,
  Real maxIncluded )
//////////////////////////////////////////////////////////////////////////////
{
  If(!_checkColumnVector("MatchRowsInInterval",M), Rand(0,0,0,0), {
  Real r = VRows(M);
  VMatrix Min = Rand(r,1,min,min);
  VMatrix Max = Rand(r,1,max,max);
  Pack(Case(
     minIncluded &  maxIncluded, And(LE(Min,M),LE(M,Max)),
    !minIncluded &  maxIncluded, And(LT(Min,M),LE(M,Max)),
     minIncluded & !maxIncluded, And(LE(Min,M),LT(M,Max)),
    !minIncluded & !maxIncluded, And(LT(Min,M),LT(M,Max))
  ),_.packSparsity)
})};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SelectRowsInInterval = 
"Selects rows of a column matrix whose cell is inside the interval defined "
"as: \n"
"  [min,max]: if minIncluded=True  and minIncluded=True\n"
"  (min,max]: if minIncluded=False and minIncluded=True\n"
"  [min,max): if minIncluded=True  and minIncluded=False\n"
"  (min,max): if minIncluded=False and minIncluded=False\n";
Set SelectRowsInInterval(
 VMatrix M, 
 Real minIncluded,
 Real min, 
 Real max,
 Real maxIncluded )
//////////////////////////////////////////////////////////////////////////////
{
  SelectMatch(MatchRowsInInterval(M,minIncluded,min,max,maxIncluded))   
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.MatchRowsInHyperCube = "Returns a column matrix with "
"True in rows of matrix M which are inside the hipercube whose side length "
"is 2*d and is centered in point given by row matrix P ";
VMatrix MatchRowsInHyperCube(VMatrix M, VMatrix P, Real d)
//////////////////////////////////////////////////////////////////////////////
{
  Real r = VRows(M);
  Real c = VColumns(M);
  VMatrix D = Rand(r,1,d,d);
  Group("And",For(1,c, VMatrix(Real k)
  {
    Pack(LE(Abs(SubCol(M,[[k]])-VMatDat(P,1,k)),D), _.packSparsity)
  }))
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SelectRowsInHyperCube = "Selects rows of matrix "
"M that are inside the hipercube whose side length is 2*d and is centered in "
"point given by row matrix P ";
Set SelectRowsInHyperCube(VMatrix M, VMatrix P, Real d)
//////////////////////////////////////////////////////////////////////////////
{
  SelectMatch(MatchRowsInHyperCube(M,P,d))   
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.MatchRowsInHyperSphere = "Returns a column matrix with "
"True in rows of matrix M which are inside the hipersphere with specified "
"ratio and is centered in point given by row matrix P";
VMatrix MatchRowsInHyperSphere(VMatrix M, VMatrix P, Real ratio)
//////////////////////////////////////////////////////////////////////////////
{
  Real r = VRows(M);
  Real c = VColumns(M);
  Real ratio2 = ratio^2;
  VMatrix R2 = Rand(r,1,ratio2,ratio2);
  VMatrix dist2 = SetSum(For(1,c, VMatrix(Real k)
  {
    RPow(SubCol(M,[[k]])-VMatDat(P,1,k),2)
  }));
  Pack(LE(dist2,R2),_.packSparsity)
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SelectRowsInHyperSphere = "Selects rows of "
"matrix M that are inside the hipersphere with specified  ratio and is "
"centered in point given by row matrix P";
Set SelectRowsInHyperSphere(VMatrix M, VMatrix P, Real ratio)
//////////////////////////////////////////////////////////////////////////////
{
  SelectMatch(MatchRowsInHyperSphere(M,P,ratio))   
};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.ClassifyRowsByRank = "Classifies the rows of a column "
"matrix by the ranking position respect a set of R sorted numbers. The class "
"of the i-th row will be assigned in this way:\n"
" class[i] = 1   <=>                 M[i] < rankBound[1]\n"
" class(i] = k+1 <=> rankBound[k] <= M[i] < rankBound[k+1]; k=1...R\n"
" class[i] = R+1 <=> rankBound[R] <= M[i]\n";
VMatrix ClassifyRowsByRank(VMatrix M, Set rankBound)
//////////////////////////////////////////////////////////////////////////////
{
  If(!_checkColumnVector("ClassifyRowsByRank",M), Rand(0,0,0,0), {
  Real m = VRows(M);
  Real R = Card(rankBound);
  Real min = 
  {
    Real aux = VMatMin(M);
    aux - Abs(aux) - 1
  };
  Real max = 
  { 
    Real aux = VMatMax(M);
    aux + Abs(aux) + 1
  };
  Set rnk = SetOfReal(min)<<rankBound<<SetOfReal(max);
  VMatrix class = Zeros(m,1);
  Real r = 1;
  VMatrix constant = Rand(m,1,1,1);
  While(r<=R+1,
  {
    VMatrix left.match = LE(constant*rnk[r],M);
    VMatrix right.match = LT(M,constant*rnk[r+1]);
    VMatrix class := class + And(left.match,right.match)*r;
    Real r := r+1
  });
  class
})};

//////////////////////////////////////////////////////////////////////////////
Text _.autodoc.member.SortAndCount = "Sorts the rows of a column matrix and "
"returns a matrix with two columns. The first one contains sorted unique "
"values found in given matrixand second one conatins the number of times "
"that this value is presented.";
VMatrix SortAndCount(VMatrix A)
//////////////////////////////////////////////////////////////////////////////
{
  Matrix B = VMat2Mat(A);
  VMatrix C = Mat2VMat(PivotByRows(B,Sort(B,[[1]])));
  Real m = VRows(C);
  Set change = Col2Set(Sort(VMat2Mat(C),[[1]],True));
  Set next = ExtractByIndex(change,Range(2,Card(change),1))<<[[Real m+1]];
  VMatrix aux.1 = DifEq(1/(1-B),Rand(m+1,1,1,1));
  VMatrix aux.2 = SubRow(aux.1,change);
  VMatrix aux.3 = SubRow(aux.1,next);
  SubRow(C,change) | (aux.3-aux.2)
}

]];
