## This file is part of MLPY.
## Canberra
   
## This code is written by Davide Albanese, <albanese@fbk.eu>.
## (C) 2009 Fondazione Bruno Kessler - Via Santa Croce 77, 38100 Trento, ITALY.

## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.

## This program 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 General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.


__all__ = ['canberra', 'canberraq', 'normalizer']

from numpy import *
import canberracore


def mod(lists, modules):
    """Arrange 'lists'
    """
    
    ret = lists.copy()
    
    for m in modules:
        tmp = sort(lists[:, m])
        ret[:, m] = tmp

    return ret


def canberra(lists, k, dist=False, modules=None):
    """Compute mean Canberra distance indicator on top-k sublists.

    Input
    
      * *lists*   - [2D numpy array integer] position lists
        Positions must be in [0, #elems-1]
      * *k*       - [integer] top-k sublists
      * *modules* - [list] modules (list of group indicies)
      * *dist*    - [bool] return partial distances (True or False)
      
    Output
    
      * *cd* - [float] canberra distance
      * *i1* - [1D numpy array integer] index 1 (if dist == True)
      * *i2* - [1D numpy array integer] index 2 (if dist == True)
      * *pd* - [1D numpy array float] partial distances for index1 and index2 (if dist == True)
      

    >>> from numpy import *
    >>> from mlpy import *
    >>> lists = array([[2,4,1,3,0],  # first positions list
    ...                [3,4,1,2,0],  # second positions list
    ...                [2,4,3,0,1],  # third positions list
    ...                [0,1,4,2,3]]) # fourth positions list
    >>> canberra(lists, 3)
    1.0861983059292479
    """

    if modules != None:
        newlists = mod(lists, modules)
    else:
        newlists = lists

    return canberracore.canberra(newlists, k, dist=dist)

    
def canberraq(lists, complete=True, normalize=False, dist=False):
    """Compute mean Canberra distance indicator on generic lists.
   
    Input
    
      * *lists* - [2D numpy array integer] position lists
        Positions must be in [-1, #elems-1],
        where -1 indicates features not present in the list
      * *complete*  - [bool] complete
      * *normalize* - [bool] normalize
      * *dist* - [bool] return partial distances (True or False)

    Output

      * *cd* - [float] canberra distance
      * *i1* - [1D numpy array integer] index 1 (if dist == True)
      * *i2* - [1D numpy array integer] index 2 (if dist == True)
      * *pd* - [1D numpy array float] partial distances for index1 and index2 (if dist == True)
    
    >>> from numpy import *
    >>> from mlpy import *
    >>> lists = array([[2,-1,1,-1,0],  # first positions list
    ...                [3,4,1,2,0],    # second positions list
    ...                [2,-1,3,0,1],   # third positions list
    ...                [0,1,4,2,3]])   # fourth positions list
    >>> canberraq(lists)
    1.0628570368721744
    """

    return canberracore.canberraq(lists, complete, normalize, dist)


def normalizer(lists):
    """Compute the average length of the partial lists (nm) and the corresponding
    normalizing factor (nf) given by 1 - a / b where a is the exact value computed
    on the average length and b is the exact value computed on the whole set of
    features.

    Inputs
    
      * *lists* - [2D numpy array integer] position lists
        Positions must be in [-1, #elems-1],
        where -1 indicates features not present in the list

    Output
    
      * *(nm, nf)* - (float, float)
    """

    return canberracore.normalizer(lists)
