#  JLMatrix - Matrix support for JRuby
#  Copyright (C) 2008 Approximatrix, LLC
#  http://www.approximatrix.com
#  
#  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/>.
#
#  NOTE: This software is licensed under the GPL and does not allow
#        linking to closed-source software.
#

import org.netlib.blas.DDOT
import org.netlib.blas.DAXPY
import org.netlib.blas.DNRM2
import org.netlib.util.intW
import org.netlib.util.doubleW

require 'matrix'

include Math

class Vector
    
    alias_method :count, :size   
    alias_method :length, :size
    
    # Provides "each" functionality
    def each
        size.times do |i|
            yield self[i]
        end
    end
    
    # Converts the vector to a java 1D array of doubles
    def to_java
        ret = Java::double[size].new
        size.times do |i|
            ret[i] = self[i]
        end
        ret
    end
    
    # Computes the dot product of this and another vector
    def dot(x)
        raise ArgumentError,"Only vectors may be used" unless x.is_a? Vector
        raise ArgumentError, "Vectors must be the same size" unless x.size == size
        #return org.netlib.blas.DDOT.DDOT(size,self.to_java,1,x.to_java,1)
        psum = 0.0
        size.times do |i|
            psum = psum + self[i]*x[i]
        end
        return psum
    end
    
    # Multiplies vectors element by element
    def multiply_elements(x)
      raise ArgumentError,"Only vectors may be used" unless x.is_a? Vector
      raise ArgumentError, "Vectors must be the same size" unless x.size == size
      ret = []
      size.times do |i|
        ret << self[i]*x[i]
      end 
      Vector.elements ret
    end
    
    # Raises all elements to a power
    def **(x)
        ret = []
        each do |ele|
            ret << (ele**x)
        end
        Vector.elements(ret)
    end
    
    # Computes the mean of this vector
    def mean
        sum/size
    end
    
    # Computes the median
    def median
    	b = to_a.sort
    	b[b.length/2]
    end
    	
    # Returns the sum of all components 
    def sum
        ret = 0.0
        each do |x|
            ret = ret + x
        end
        ret
    end
    
    # Returns the sum squared of all components 
    def sum_squared
        ret = 0.0
        each do |x|
            ret = ret + x*x
        end
        ret
    end
    
    # Implements division by a scalar
    def /(value)
    	raise ArgumentError,"Only scalars may be divisors of Vectors" unless (value.is_a? Fixnum or value.is_a? Bignum or value.is_a? Float)
    	ret = []
    	each do |x|
    		ret << (x / value)
    	end
    	Vector.elements ret
    end
      
    # Computes the standard deviation of this vector
    def standard_deviation
        result_vector = to_java
    
        org.netlib.blas.DAXPY.DAXPY(size,-1.0*mean,
                    Vector.ones(size).to_java,1,result_vector,1);
                    
        stddev_denom = 1.0;
        if(result_vector.length == 1)
          stddev_denom = 1.0;
        else
          stddev_denom = Math.sqrt(1.0*(result_vector.length-1.0))
        end
    
        return org.netlib.blas.DNRM2.DNRM2(size,result_vector,1)/stddev_denom
    end
    
    # Returns a vector sorted into ascending values
    def sort
        Vector.elements(self.to_a.sort)
    end   
    
    # Creates a vector of given length with ones for all entries
    def Vector.ones(x)
        values = []
        x.times do 
            values << 1.0          
        end
        Vector.elements(values)
    end
  
    # Creates a vector of given length with zeroes for all entries
    def Vector.zeroes(x)
        values = []
        x.times do 
            values << 0.0          
        end
        Vector.elements(values)
    end
    
    # Creates a vector from a 1D Java array
    def Vector.from_java(x)
        values = []
        begin
            x.length.times do |i|
                values << x[i]
            end
            return Vector.elements values
        rescue
            raise ArgumentError,"Input must be a valid Java array."
        end      
    end
  
end
