require 'spec_helper.rb'

describe Daru::Vector do
  ALL_DTYPES.each do |dtype|
    describe dtype.to_s do
      before do
        @common_all_dtypes =  Daru::Vector.new(
          [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99],
          dtype: dtype, name: :common_all_dtypes)
      end

      context "#initialize" do
        before do
          @tuples = [
            [:a, :one, :foo],
            [:a, :two, :bar],
            [:b, :one, :bar],
            [:b, :two, :baz]
          ]

          @multi_index = Daru::MultiIndex.from_tuples(@tuples)
        end

        it "initializes from an Array" do
          dv = Daru::Vector.new [1,2,3,4,5], name: :ravan,
            index: [:ek, :don, :teen, :char, :pach], dtype: dtype

          expect(dv.name) .to eq(:ravan)
          expect(dv.index).to eq(Daru::Index.new [:ek, :don, :teen, :char, :pach])
        end

        it "accepts Index object" do
          idx = Daru::Index.new [:yoda, :anakin, :obi, :padme, :r2d2]
          dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, index: idx, dtype: dtype

          expect(dv.name) .to eq(:yoga)
          expect(dv.index).to eq(idx)
        end

        it "accepts a MultiIndex object" do
          dv = Daru::Vector.new [1,2,3,4], name: :mi, index: @multi_index, dtype: dtype

          expect(dv.name).to eq(:mi)
          expect(dv.index).to eq(@multi_index)
        end

        it "raises error for improper Index" do
          expect {
            dv = Daru::Vector.new [1,2,3,4,5], name: :yoga, index: [:i, :j, :k]
          }.to raise_error

          expect {
            idx = Daru::Index.new [:i, :j, :k]
            dv  = Daru::Vector.new [1,2,3,4,5], name: :yoda, index: idx, dtype: dtype
          }.to raise_error
        end

        it "raises error for improper MultiIndex" do
          expect {
            dv = Daru::Vector.new [1,2,3,4,5], name: :mi, index: @multi_index
          }.to raise_error
        end

        it "initializes without specifying an index" do
          dv = Daru::Vector.new [1,2,3,4,5], name: :vishnu, dtype: dtype

          expect(dv.index).to eq(Daru::Index.new [0,1,2,3,4])
        end

        it "inserts nils for extra indices" do
          dv = Daru::Vector.new [1,2,3], name: :yoga, index: [0,1,2,3,4], dtype: dtype

          expect(dv).to eq([1,2,3,nil,nil].dv(:yoga,nil, :array))
        end

        it "inserts nils for extra indices (MultiIndex)" do
          dv = Daru::Vector.new [1,2], name: :mi, index: @multi_index, dtype: :array
          expect(dv).to eq(Daru::Vector.new([1,2,nil,nil], name: :mi, index: @multi_index, dtype: :array))
        end

        it "accepts all sorts of objects for indexing" do
          dv = Daru::Vector.new [1,2,3,4], index: ['a', 'b', :r, 0]
          expect(dv.to_a).to eq([1,2,3,4])
          expect(dv.index.to_a).to eq(['a', 'b', :r, 0])
        end
      end

      context ".new_with_size" do
        it "creates new vector from only size" do
          v1 = Daru::Vector.new 10.times.map { nil }, dtype: dtype
          v2 = Daru::Vector.new_with_size 10, dtype: dtype
          expect(v2).to eq(v1)
        end if [:array, :nmatrix].include?(dtype)

        it "creates new vector from only size and value" do
          a = rand
          v1 = Daru::Vector.new 10.times.map { a }, dtype: dtype
          v2 = Daru::Vector.new_with_size(10, value: a, dtype: dtype)
          expect(v2).to eq(v1)
        end

        it "accepts block" do
          v1 = Daru::Vector.new 10.times.map {|i| i * 2 }
          v2 = Daru::Vector.new_with_size(10, dtype: dtype) { |i| i * 2 }
          expect(v2).to eq(v1)
        end
      end

      context ".[]" do
        it "returns same results as R-c()" do
          reference = Daru::Vector.new([0, 4, 5, 6, 10])
          expect(Daru::Vector[0, 4, 5, 6, 10])          .to eq(reference)
          expect(Daru::Vector[0, 4..6, 10])             .to eq(reference)
          expect(Daru::Vector[[0], [4, 5, 6], [10]])    .to eq(reference)
          expect(Daru::Vector[[0], [4, [5, [6]]], [10]]).to eq(reference)

          expect(Daru::Vector[[0], Daru::Vector.new([4, 5, 6]), [10]])
                                                        .to eq(reference)
        end
      end

      context "#[]" do
        context Daru::Index do
          before :each do
            @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga,
              index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype
          end

          it "returns an element after passing an index" do
            expect(@dv[:yoda]).to eq(1)
          end

          it "returns an element after passing a numeric index" do
            expect(@dv[0]).to eq(1)
          end

          it "returns a vector with given indices for multiple indices" do
            expect(@dv[:yoda, :anakin]).to eq(Daru::Vector.new([1,2], name: :yoda,
              index: [:yoda, :anakin], dtype: dtype))
          end

          it "returns a vector with given indices for multiple numeric indices" do
            expect(@dv[0,1]).to eq(Daru::Vector.new([1,2], name: :yoda,
              index: [:yoda, :anakin], dtype: dtype))
          end

          it "returns a vector when specified symbol Range" do
            expect(@dv[:yoda..:anakin]).to eq(Daru::Vector.new([1,2],
              index: [:yoda, :anakin], name: :yoga, dtype: dtype))
          end

          it "returns a vector when specified numeric Range" do
            expect(@dv[3..4]).to eq(Daru::Vector.new([4,5], name: :yoga,
              index: [:padme, :r2d2], dtype: dtype))
          end

          it "returns correct results for index of multiple index" do
            v = Daru::Vector.new([1,2,3,4], index: ['a','c',1,:a])
            expect(v['a']).to eq(1)
            expect(v[:a]).to eq(4)
            expect(v[1]).to eq(3)
            expect(v[0]).to eq(1)
          end
        end

        context Daru::MultiIndex do
          before do
            @tuples = [
              [:a,:one,:bar],
              [:a,:one,:baz],
              [:a,:two,:bar],
              [:a,:two,:baz],
              [:b,:one,:bar],
              [:b,:two,:bar],
              [:b,:two,:baz],
              [:b,:one,:foo],
              [:c,:one,:bar],
              [:c,:one,:baz],
              [:c,:two,:foo],
              [:c,:two,:bar]
            ]
            @multi_index = Daru::MultiIndex.from_tuples(@tuples)
            @vector = Daru::Vector.new(
              Array.new(12) { |i| i }, index: @multi_index,
              dtype: dtype, name: :mi_vector)
          end

          it "returns a single element when passed a row number" do
            expect(@vector[1]).to eq(1)
          end

          it "returns a single element when passed the full tuple" do
            expect(@vector[:a, :one, :baz]).to eq(1)
          end

          it "returns sub vector when passed first layer of tuple" do
            mi = Daru::MultiIndex.from_tuples([
              [:one,:bar],
              [:one,:baz],
              [:two,:bar],
              [:two,:baz]])
            expect(@vector[:a]).to eq(Daru::Vector.new([0,1,2,3], index: mi,
              dtype: dtype, name: :sub_vector))
          end

          it "returns sub vector when passed first and second layer of tuple" do
            mi = Daru::MultiIndex.from_tuples([
              [:foo],
              [:bar]])
            expect(@vector[:c,:two]).to eq(Daru::Vector.new([10,11], index: mi,
              dtype: dtype, name: :sub_sub_vector))
          end

          it "returns a vector with corresponding MultiIndex when specified numeric Range" do
            mi = Daru::MultiIndex.from_tuples([
              [:a,:two,:baz],
              [:b,:one,:bar],
              [:b,:two,:bar],
              [:b,:two,:baz],
              [:b,:one,:foo],
              [:c,:one,:bar],
              [:c,:one,:baz]
            ])
            expect(@vector[3..9]).to eq(Daru::Vector.new([3,4,5,6,7,8,9], index: mi,
              dtype: dtype, name: :slice))
          end
        end
      end

      context "#[]=" do
        context Daru::Index do
          before :each do
            @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga,
              index: [:yoda, :anakin, :obi, :padme, :r2d2], dtype: dtype
          end

          it "assigns at the specified index" do
            @dv[:yoda] = 666
            expect(@dv[:yoda]).to eq(666)
          end

          it "assigns at the specified Integer index" do
            @dv[0] = 666
            expect(@dv[:yoda]).to eq(666)
          end

          it "sets dtype to Array if a nil is assigned" do
            @dv[0] = nil
            expect(@dv.dtype).to eq(:array)
          end

          it "assigns correctly for a mixed index Vector" do
            v = Daru::Vector.new [1,2,3,4], index: ['a',:a,0,66]
            v['a'] = 666
            expect(v['a']).to eq(666)

            v[0] = 666
            expect(v[0]).to eq(666)

            v[3] = 666
            expect(v[3]).to eq(666)

            expect(v).to eq(Daru::Vector.new([666,2,666,666],
              index: ['a',:a,0,66]))
          end
        end

        context Daru::MultiIndex do
          before :each do
            @tuples = [
              [:a,:one,:bar],
              [:a,:one,:baz],
              [:a,:two,:bar],
              [:a,:two,:baz],
              [:b,:one,:bar],
              [:b,:two,:bar],
              [:b,:two,:baz],
              [:b,:one,:foo],
              [:c,:one,:bar],
              [:c,:one,:baz],
              [:c,:two,:foo],
              [:c,:two,:bar]
            ]
            @multi_index = Daru::MultiIndex.from_tuples(@tuples)
            @vector = Daru::Vector.new Array.new(12) { |i| i }, index: @multi_index,
              dtype: dtype, name: :mi_vector
          end

          it "assigns all lower layer indices when specified a first layer index" do
            @vector[:b] = 69
            expect(@vector).to eq(Daru::Vector.new([0,1,2,3,69,69,69,69,8,9,10,11],
              index: @multi_index, name: :top_layer_assignment, dtype: dtype
              ))
          end

          it "assigns all lower indices when specified first and second layer index" do
            @vector[:b, :one] = 69
            expect(@vector).to eq(Daru::Vector.new([0,1,2,3,69,5,6,69,8,9,10,11],
              index: @multi_index, name: :second_layer_assignment, dtype: dtype))
          end

          it "assigns just the precise value when specified complete tuple" do
            @vector[:b, :one, :foo] = 69
            expect(@vector).to eq(Daru::Vector.new([0,1,2,3,4,5,6,69,8,9,10,11],
              index: @multi_index, name: :precise_assignment, dtype: dtype))
          end

          it "assigns correctly when numeric index" do
            @vector[7] = 69
            expect(@vector).to eq(Daru::Vector.new([0,1,2,3,4,5,6,69,8,9,10,11],
              index: @multi_index, name: :precise_assignment, dtype: dtype))
          end
        end
      end

      context "#concat" do
        before :each do
          @dv = Daru::Vector.new [1,2,3,4,5], name: :yoga,
            index: [:warwick, :thompson, :jackson, :fender, :esp], dtype: dtype
        end

        it "concatenates a new element at the end of vector with index" do
          @dv.concat 6, :ibanez

          expect(@dv.index)   .to eq(
            Daru::Index.new([:warwick, :thompson, :jackson, :fender, :esp, :ibanez]))
          expect(@dv[:ibanez]).to eq(6)
          expect(@dv[5])      .to eq(6)
        end

        it "raises error if index not specified" do
          expect {
            @dv.concat 6
          }.to raise_error
        end
      end

      context "#delete" do
        context Daru::Index do
          it "deletes specified value in the vector" do
            dv = Daru::Vector.new [1,2,3,4,5], name: :a, dtype: dtype

            dv.delete 3
            expect(dv).to eq(
              Daru::Vector.new [1,2,4,5], name: :a, index: [0,1,3,4])
          end
        end
      end

      context "#delete_at" do
        context Daru::Index do
          before :each do
            @dv = Daru::Vector.new [1,2,3,4,5], name: :a,
              index: [:one, :two, :three, :four, :five], dtype: dtype
          end

          it "deletes element of specified index" do
            @dv.delete_at :one

            expect(@dv).to eq(Daru::Vector.new [2,3,4,5], name: :a,
              index: [:two, :three, :four, :five], dtype: dtype)
          end

          it "deletes element of specified integer index" do
            pending
            @dv.delete_at 2

            expect(@dv).to eq(Daru::Vector.new [1,2,4,5], name: :a,
              index: [:one, :two, :four, :five], dtype: dtype)
          end
        end
      end

      context "#delete_if" do
        it "deletes elements if block evaluates to true" do
          v = Daru::Vector.new [1,22,33,45,65,32,524,656,123,99,77], dtype: dtype
          ret = v.delete_if { |d| d % 11 == 0 }
          expect(ret).to eq(
            Daru::Vector.new([1,45,65,32,524,656,123],
              index: [0,3,4,5,6,7,8], dtype: dtype))
          expect(ret.dtype).to eq(dtype)
        end
      end

      context "#keep_if" do
        it "keeps elements if block returns true" do
          v = Daru::Vector.new([1,22,33,45,65,32,524,656,123,99,77], dtype: dtype)
          ret = v.keep_if { |d| d < 35 }

          expect(ret).to eq(
            Daru::Vector.new([1,22,33,32], index: [0,1,2,5], dtype: dtype))
          expect(v.dtype).to eq(ret.dtype)
        end
      end

      context "#index_of" do
        context Daru::Index do
          it "returns index of specified value" do
            dv = Daru::Vector.new [1,2,3,4,5], name: :a,
              index: [:one, :two, :three, :four, :five], dtype: dtype

            expect(dv.index_of(1)).to eq(:one)
          end
        end

        context Daru::MultiIndex do
          it "returns tuple of specified value" do
            mi = Daru::MultiIndex.from_tuples([
              [:a,:two,:bar],
              [:a,:two,:baz],
              [:b,:one,:bar],
              [:b,:two,:bar]
            ])
            vector = Daru::Vector.new([1,2,3,4], index: mi, dtype: dtype)
            expect(vector.index_of(3)).to eq([:b,:one,:bar])
          end
        end
      end

      context "#to_hash" do
        context Daru::Index do
          it "returns the vector as a hash" do
            dv = Daru::Vector.new [1,2,3,4,5], name: :a,
              index: [:one, :two, :three, :four, :five], dtype: dtype

            expect(dv.to_hash).to eq({one: 1, two: 2, three: 3, four: 4, five: 5})
          end
        end

        context Daru::MultiIndex do
          pending
          # it "returns vector as a Hash" do
          #   pending
          #   mi = Daru::MultiIndex.from_tuples([
          #     [:a,:two,:bar],
          #     [:a,:two,:baz],
          #     [:b,:one,:bar],
          #     [:b,:two,:bar]
          #   ])
          #   vector = Daru::Vector.new([1,2,3,4], index: mi, dtype: dtype)
          #   expect(vector.to_hash).to eq({
          #     [:a,:two,:bar] => 1,
          #     [:a,:two,:baz] => 2,
          #     [:b,:one,:bar] => 3,
          #     [:b,:two,:bar] => 4
          #   })
          # end
        end
      end

      context "#uniq" do
        it "keeps only unique values" do
          # TODO: fill this in
        end
      end

      context "#cast" do
        ALL_DTYPES.each do |new_dtype|
          it "casts from #{dtype} to #{new_dtype}" do
            v = Daru::Vector.new [1,2,3,4], dtype: dtype
            v.cast(dtype: new_dtype)
            expect(v.dtype).to eq(new_dtype)
          end
        end
      end

      context "#sort" do
        context Daru::Index do
          before do
            @dv = Daru::Vector.new [33,2,15,332,1], name: :dv, index: [:a, :b, :c, :d, :e]
          end

          it "sorts the vector with defaults and returns a new vector, preserving indexing" do
            expect(@dv.sort).to eq(Daru::Vector.new([1,2,15,33,332], name: :dv, index: [:e, :b, :c, :a, :d]))
          end

          it "sorts the vector in descending order" do
            expect(@dv.sort(ascending: false)).to eq(Daru::Vector.new([332,33,15,2,1], name: :dv, index: [:d, :a, :c, :b, :e]))
          end

          it "accepts a block" do
            str_dv = Daru::Vector.new ["My Jazz Guitar", "Jazz", "My", "Guitar"]

            sorted = str_dv.sort { |a,b| a.length <=> b.length }
            expect(sorted).to eq(Daru::Vector.new(["My", "Jazz", "Guitar", "My Jazz Guitar"], index: [2,1,3,0]))
          end

          it "places nils near the beginning of the vector" do
            with_nils = Daru::Vector.new [22,4,nil,111,nil,2]

            expect(with_nils.sort).to eq(Daru::Vector.new([nil,nil,2,4,22,111], index: [2,4,5,1,0,3]))
          end if dtype == :array
        end

        context Daru::MultiIndex do
          before do
            mi = Daru::MultiIndex.from_tuples([
              [:a, :one,   :foo],
              [:a, :two,   :bar],
              [:b, :one,   :bar],
              [:b, :two,   :baz],
              [:b, :three, :bar]
              ])
            @vector = Daru::Vector.new([44,22,111,0,-56], index: mi, name: :unsorted,
              dtype: dtype)
          end

          it "sorts vector" do
            mi_asc = Daru::MultiIndex.from_tuples([
              [:b, :three, :bar],
              [:b, :two,   :baz],
              [:a, :two,   :bar],
              [:a, :one,   :foo],
              [:b, :one,   :bar]
            ])
            expect(@vector.sort).to eq(Daru::Vector.new([-56,0,22,44,111], index: mi_asc,
              name: :ascending, dtype: dtype))
          end

          it "sorts in descending" do
            mi_dsc = Daru::MultiIndex.from_tuples([
              [:b, :one, :bar],
              [:a, :one, :foo],
              [:a, :two, :bar],
              [:b, :two, :baz],
              [:b, :three, :bar]
            ])
            expect(@vector.sort(ascending: false)).to eq(Daru::Vector.new(
              [111,44,22,0,-56], index: mi_dsc, name: :descending, dtype: dtype))
          end

          it "sorts using the supplied block" do
            mi_abs = Daru::MultiIndex.from_tuples([
              [:b, :two,   :baz],
              [:a, :two,   :bar],
              [:a, :one,   :foo],
              [:b, :three, :bar],
              [:b, :one,   :bar]
            ])
            expect(@vector.sort { |a,b| a.abs <=> b.abs }).to eq(Daru::Vector.new(
              [0,22,44,-56,111], index: mi_abs, name: :sort_abs, dtype: dtype))
          end
        end
      end

      context "#index=" do
        before do
          @vector = Daru::Vector.new([1,2,3,4,5])
        end

        it "simply reassigns index" do
          index  = Daru::DateTimeIndex.date_range(:start => '2012', :periods => 5)
          @vector.index = index

          expect(@vector.index.class).to eq(DateTimeIndex)
          expect(@vector['2012-1-1']).to eq(1)
        end

        it "raises error for index size != vector size" do
          expect {
            @vector.index = Daru::Index.new([4,2,6])
          }.to raise_error
        end
      end

      context "#reindex" do
        it "intelligently reindexes" do
          vector = Daru::Vector.new([1,2,3,4,5])
          index = Daru::Index.new([3,4,1,0,6])

          expect(vector.reindex(index)).to eq(
            Daru::Vector.new([4,5,2,1,nil], index: index))
        end
      end

      context "#collect" do
        it "returns an Array" do
          a = @common_all_dtypes.collect { |v| v }
          expect(a).to eq([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99])
        end
      end

      context "#map" do
        it "maps" do
          a = @common_all_dtypes.map { |v| v }
          expect(a).to eq([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99])
        end
      end

      context "#map!" do
        it "destructively maps" do
          @common_all_dtypes.map! { |v| v + 1 }
          expect(@common_all_dtypes).to eq(Daru::Vector.new(
            [6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 2, 3, 4, 5, 12, -98, -98],
            dtype: dtype))
        end
      end

      context "#recode" do
        it "maps and returns a vector of dtype of self by default" do
          a = @common_all_dtypes.recode { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
          expect(a).to eq(exp)
          expect(a.dtype).to eq(:array)
        end

        it "maps and returns a vector of dtype gsl" do
          a = @common_all_dtypes.recode(:gsl) { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], dtype: :gsl
          expect(a).to eq(exp)
          expect(a.dtype).to eq(:gsl)
        end

        it "maps and returns a vector of dtype nmatrix" do
          a = @common_all_dtypes.recode(:nmatrix) { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], dtype: :nmatrix
          expect(a).to eq(exp)
          expect(a.dtype).to eq(:nmatrix)
        end
      end

      context "#recode!" do
        before :each do
          @vector =  Daru::Vector.new(
            [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, 11, -99, -99],
            dtype: dtype, name: :common_all_dtypes)
        end

        it "destructively maps and returns a vector of dtype of self by default" do
          @vector.recode! { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
          expect(@vector).to eq(exp)
          expect(@vector.dtype).to eq(dtype)
        end

        it "destructively maps and returns a vector of dtype gsl" do
          @vector.recode!(:gsl) { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], dtype: :gsl
          expect(@vector).to eq(exp)
          expect(@vector.dtype).to eq(exp.dtype)
        end

        it "destructively maps and returns a vector of dtype nmatrix" do
          @vector.recode!(:nmatrix) { |v| v == -99 ? 1 : 0 }
          exp = Daru::Vector.new [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], dtype: :nmatrix
          expect(@vector).to eq(exp)
          expect(@vector.dtype).to eq(exp.dtype)
        end
      end

      context "#verify" do
        it "returns a hash of invalid data and index of data" do
          v = Daru::Vector.new [1,2,3,4,5,6,-99,35,-100], dtype: dtype
          h = v.verify { |d| d > 0 }
          e = { 6 => -99, 8 => -100 }
          expect(h).to eq(e)
        end
      end

      context "#summary" do
        it "has name in the summary" do
          expect(@common_all_dtypes.summary.match("#{@common_all_dtypes.name}")).to_not eq(nil)
        end
      end

      context "#bootstrap" do
        it "returns a vector with mean=mu and sd=se" do
          rng = Distribution::Normal.rng(0, 1)
          vector =Daru::Vector.new_with_size(100, dtype: dtype) { rng.call}

          df = vector.bootstrap([:mean, :sd], 200)
          se = 1 / Math.sqrt(vector.size)
          expect(df[:mean].mean).to be_within(0.3).of(0)
          expect(df[:mean].sd).to be_within(0.02).of(se)
        end
      end
    end
  end # describe ALL_DTYPES.each

  # -----------------------------------------------------------------------
  # works with arrays only

  context "#splitted" do
    it "splits correctly" do
      a = Daru::Vector.new ['a', 'a,b', 'c,d', 'a,d', 'd', 10, nil]
      expect(a.splitted).to eq([%w(a), %w(a b), %w(c d), %w(a d), %w(d), [10], nil])
    end
  end

  context "#missing_values" do
    before do
      @common = Daru::Vector.new([5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99])
    end

    it "allows setting the value to be treated as missing" do
      @common.missing_values = [10]
      expect(@common.only_valid.to_a.sort).to eq(
        [-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9]
      )
      expect(@common.to_a).to eq(
        [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]
      )

      @common.missing_values = [-99]
      expect(@common.only_valid.to_a.sort).to eq(
        [1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]
      )
      expect(@common.to_a).to eq(
        [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]
      )

      @common.missing_values = []
      expect(@common.only_valid.to_a.sort).to eq(
        [-99, -99, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10]
      )
      expect(@common.to_a).to eq(
        [5, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 1, 2, 3, 4, nil, -99, -99]
      )
    end

    it "responds to has_missing_data? with explicit missing_values" do
      a = Daru::Vector.new [1,2,3,4,10]
      a.missing_values = [10]

      expect(a.has_missing_data?).to eq(true)
    end
  end

  context "#is_nil?" do
    before(:each) do
      @with_md    = Daru::Vector.new([1,2,nil,3,4,nil])
      @without_md = Daru::Vector.new([1,2,3,4,5,6])
    end

    it "verifies missing data presence" do
      expect(@with_md.is_nil?)   .to eq(Daru::Vector.new([false,false,true,false,false,true]))
      expect(@without_md.is_nil?).to eq(Daru::Vector.new([false,false,false,false,false,false]))
    end
  end

  context "#clone_structure" do
    context Daru::Index do
      it "clones a vector with its index and fills it with nils" do
        vec = Daru::Vector.new([1,2,3,4,5], index: [:a,:b,:c,:d,:e])
        expect(vec.clone_structure).to eq(Daru::Vector.new([nil,nil,nil,nil,nil], index: [:a,:b,:c,:d,:e]))
      end
    end

    context Daru::MultiIndex do
      pending
    end
  end

  context "#missing_positions" do
    context Daru::Index do
      before(:each) do
        @with_md = Daru::Vector.new([1,2,nil,3,4,nil])
      end

      it "returns the indexes of nils" do
        expect(@with_md.missing_positions).to eq([2,5])
      end

      it "updates after assingment" do
        @with_md[3] = nil
        expect(@with_md.missing_positions).to eq([2,3,5])
      end
    end

    context Daru::MultiIndex do
      it "returns indexes of nils" do
        mi = Daru::MultiIndex.from_tuples([
          ['M', 2000],
          ['M', 2001],
          ['M', 2002],
          ['M', 2003],
          ['F', 2000],
          ['F', 2001],
          ['F', 2002],
          ['F', 2003]
          ])
        vector = Daru::Vector.new([nil,2,4,5,3,nil,2,nil], index: mi)
        expect(vector.missing_positions).to eq([
          ['M',2000],
          ['F',2001],
          ['F',2003]
        ])
      end
    end
  end

  context "#replace_nils" do
    it "replaces all nils with the specified value" do
      vec = Daru::Vector.new([1,2,3,nil,nil,4])
      expect(vec.replace_nils(2)).to eq(Daru::Vector.new([1,2,3,2,2,4]))
    end

    it "replaces all nils with the specified value (bang)" do
      vec = Daru::Vector.new([1,2,3,nil,nil,4]).replace_nils!(2)
      expect(vec).to eq(Daru::Vector.new([1,2,3,2,2,4]))
    end
  end

  context "#type" do
    before(:each) do
      @numeric    = Daru::Vector.new([1,2,3,4,5])
      @multi      = Daru::Vector.new([1,2,3,'sameer','d'])
      @with_nils  = Daru::Vector.new([1,2,3,4,nil])
    end

    it "checks numeric data correctly" do
      expect(@numeric.type).to eq(:numeric)
    end

    it "checks for multiple types of data" do
      expect(@multi.type).to eq(:object)
    end

    it "tells NMatrix data type in case of NMatrix wrapper" do
      nm = Daru::Vector.new([1,2,3,4,5], dtype: :nmatrix)
      expect(nm.type).to eq(:int32)
    end

    it "changes type to object as per assignment" do
      expect(@numeric.type).to eq(:numeric)
      @numeric[2] = 'my string'
      expect(@numeric.type).to eq(:object)
    end

    it "changes type to numeric as per assignment" do
      expect(@multi.type).to eq(:object)
      @multi[3] = 45
      @multi[4] = 54
      expect(@multi.type).to eq(:numeric)
    end

    it "reports numeric if nils with number data" do
      expect(@with_nils.type).to eq(:numeric)
    end

    it "stays numeric when nil is reassigned to a number" do
      @with_nils[4] = 66
      expect(@with_nils.type).to eq(:numeric)
    end

    it "changes to :object when nil is reassigned to anything but a number" do
      @with_nils[4] = 'string'
      expect(@with_nils.type).to eq(:object)
    end
  end

  context "#to_matrix" do
    before do
      @vector = Daru::Vector.new [1,2,3,4,5,6]
    end

    it "converts Daru::Vector to a horizontal Ruby Matrix" do
      expect(@vector.to_matrix).to eq(Matrix[[1,2,3,4,5,6]])
    end

    it "converts Daru::Vector to a vertical Ruby Matrix" do
      expect(@vector.to_matrix(:vertical)).to eq(Matrix.columns([[1,2,3,4,5,6]]))
    end
  end

  context "#only_valid" do
    it "returns a Vector of only non-nil data" do
      vector = Daru::Vector.new [1,2,3,4,nil,3,nil],
        index: [:a, :b, :c, :d, :e, :f, :g]
      expect(vector.only_valid).to eq(Daru::Vector.new([1,2,3,4,3],
        index: [:a, :b, :c, :d, :f]))
    end
  end

  context "#only_numerics" do
    it "returns only numerical or missing data" do
      v = Daru::Vector.new([1,2,nil,3,4,'s','a',nil])
      expect(v.only_numerics).to eq(Daru::Vector.new([1,2,nil,3,4,nil],
        index: [0,1,2,3,4,7]))
    end
  end

  context "#to_gsl" do
    it "returns a GSL::Vector of non-nil data" do
      vector = Daru::Vector.new [1,2,3,4,nil,6,nil]
      expect(vector.to_gsl).to eq(GSL::Vector.alloc(1,2,3,4,6))

      gsl_vec = Daru::Vector.new [1,2,3,4,5], dtype: :gsl
      expect(gsl_vec.to_gsl).to eq(GSL::Vector.alloc(1,2,3,4,5))
    end
  end

  context "#split_by_separator" do
    def expect_correct_tokens hash
      expect(hash['a'].to_a).to eq([1, 1, 0, 1, 0, nil])
      expect(hash['b'].to_a).to eq([0, 1, 0, 0, 0, nil])
      expect(hash['c'].to_a).to eq([0, 0, 1, 0, 0, nil])
      expect(hash['d'].to_a).to eq([0, 0, 1, 1, 0, nil])
      expect(hash[10].to_a).to eq([0, 0, 0, 0, 1, nil])
    end

    before do
      @a = Daru::Vector.new ['a', 'a,b', 'c,d', 'a,d', 10, nil]
      @b = @a.split_by_separator(',')
    end

    it "returns a Hash" do
      expect(@b.class).to eq(Hash)
    end

    it "returned Hash has keys with with different values of @a" do
      expect(@b.keys).to eq(['a', 'b', 'c', 'd', 10])
    end

    it "returns a Hash, whose values are Daru::Vector" do
      @b.each_key do |key|
        expect(@b[key].class).to eq(Daru::Vector)
      end
    end

    it "ensures that hash values are n times the tokens appears" do
      expect_correct_tokens @b
    end

    it "gives the same values using a different separator" do
      a = Daru::Vector.new ['a', 'a*b', 'c*d', 'a*d', 10, nil]
      b = a.split_by_separator '*'
      expect_correct_tokens b
    end
  end

  context "#split_by_separator_freq" do
    it "returns the number of ocurrences of tokens" do
      a = Daru::Vector.new ['a', 'a,b', 'c,d', 'a,d', 10, nil]
      expect(a.split_by_separator_freq).to eq(
        { 'a' => 3, 'b' => 1, 'c' => 1, 'd' => 2, 10 => 1 })
    end
  end

  context "#n_valid" do
    it "returns number of non-missing positions" do
      v = Daru::Vector.new [1,2,3,4,nil,nil,3,5]
      expect(v.n_valid).to eq(6)
    end
  end

  context "#reset_index!" do
    it "resets any index to a numerical serialized index" do
      v = Daru::Vector.new([1,2,3,4,5,nil,nil,4,nil])
      r = v.only_valid.reset_index!
      expect(r).to eq(Daru::Vector.new([1,2,3,4,5,4]))
      expect(r.index).to eq(Daru::Index.new([0,1,2,3,4,5]))

      indexed = Daru::Vector.new([1,2,3,4,5], index: [:a, :b, :c, :d, :e])
      expect(indexed.reset_index!.index).to eq(Daru::Index.new([0,1,2,3,4]))
    end
  end

  context "#rename" do
    before :each do
      @v = Daru::Vector.new [1,2,3,4,5,5], name: :this_vector
    end

    it "assings name" do
      @v.rename :that_vector
      expect(@v.name).to eq(:that_vector)
    end

    it "stores name as a symbol" do
      @v.rename "This is a vector"
      expect(@v.name).to eq("This is a vector")
    end
  end

  context "#any?" do
    before do
      @v = Daru::Vector.new([1,2,3,4,5])
    end

    it "returns true if block returns true for any one of the elements" do
      expect(@v.any?{ |e| e == 1 }).to eq(true)
    end

    it "returns false if block is false for all elements" do
      expect(@v.any?{ |e| e > 10 }).to eq(false)
    end
  end

  context "#all?" do
    before do
      @v = Daru::Vector.new([1,2,3,4,5])
    end

    it "returns true if block is true for all elements" do
      expect(@v.all? { |e| e < 6 }).to eq(true)
    end

    it "returns false if block is false for any one element" do
      expect(@v.all? { |e| e == 2 }).to eq(false)
    end
  end

  context "#only_missing" do
    it "returns a vector (with proper index) of all the elements marked 'missing'" do
      v = Daru::Vector.new([1,2,3,4,5,6,4,5,5,4,4,nil,nil,nil])
      v.missing_values = [nil, 5]

      expect(v.only_missing).to eq(Daru::Vector.new([5,5,5,nil,nil,nil],
        index: [4,7,8,11,12,13]))
    end
  end

  context "#detach_index" do
    it "creates a DataFrame with first Vector as index and second as values of the Vector" do
      v = Daru::Vector.new([1,2,3,4,5,6],
        index: ['a', 'b', 'c', 'd', 'e', 'f'], name: :values)
      expect(v.detach_index).to eq(Daru::DataFrame.new({
        index: ['a', 'b', 'c', 'd', 'e', 'f'],
        values: [1,2,3,4,5,6]
      }))
    end
  end

  context "#lag" do
    it "lags the vector by specified amount" do
      xiu = Daru::Vector.new([17.28, 17.45, 17.84, 17.74, 17.82, 17.85, 17.36, 17.3, 17.56, 17.49, 17.46, 17.4, 17.03, 17.01,
        16.86, 16.86, 16.56, 16.36, 16.66, 16.77])
      lag1 = xiu.lag

      expect(lag1[lag1.size - 1]).to be_within(0.001).of(16.66)
      expect(lag1[lag1.size - 2]).to be_within(0.001).of(16.36)

      #test with different lagging unit
      lag2 = xiu.lag(2)

      expect(lag2[lag2.size - 1]).to be_within(0.001).of(16.36)
      expect(lag2[lag2.size - 2]).to be_within(0.001).of(16.56)
    end
  end
end if mri?