signed_multiset 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 09a3ae9f761ec896d77d62a7a691f9fe65a20f09
4
+ data.tar.gz: 0f66bdd5eb798fece35bcd588bc90a50e1c22d81
5
+ SHA512:
6
+ metadata.gz: a30b92054d69d03ebfa1947fd8505f81ce57d7f1230095ec2ffff274910232a26596c8af4df9bfba87cacaab441e6704cc9b1fcde7477969b4411282628dc382
7
+ data.tar.gz: 45684d6839b35ffe4c26073fd4d026064b2aa251ef8aee35eb07c1408ab1b4cfbf97d3e073dd2f4d098861a05247c64c4b8bbea4dfeeb3233ce5814ce3b3dd89
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1 @@
1
+ 2.0.0-p195
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in signed_multiset.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Josh Lewis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # signed_multiset
2
+
3
+ [![Build Status](https://travis-ci.org/joshwlewis/signed_multiset.png?branch=master)](https://travis-ci.org/joshwlewis/signed_multiset)
4
+ [![Code Climate](https://codeclimate.com/github/joshwlewis/signed_multiset.png)](https://codeclimate.com/github/joshwlewis/signed_multiset)
5
+ [![Dependency Status](https://gemnasium.com/joshwlewis/signed_multiset.png)](https://gemnasium.com/joshwlewis/signed_multiset)
6
+
7
+ Signed Multiset is a Ruby implementation of a Multiset that allows negative membership.
8
+
9
+ You can think of it as a Multiset or Bag that allows for negative counts. It feels like a Ruby Hash or Array, but with some differences:
10
+
11
+ - A key (any ruby object) can be added to or removed from the Multiset any number of times, and is referred to as it's multiplicity
12
+
13
+ - A key can only be considered a member of the Signed Multiset if it's multiplicity is not 0. Setting it's multiplicity to 0 effectively removes that item from the SignedMultiset.
14
+
15
+ - The size or length of a SignedMultiset is the number of unique keys with non-zero multiplicities.
16
+
17
+ - The cardinality or sum of a SignedMultiset is the total sum of the multiplicities.
18
+
19
+ For theory and proofs, refer to [Negative Membership](http://projecteuclid.org/DPubS/Repository/1.0/Disseminate?view=body&id=pdf_1&handle=euclid.ndjfl/1093635499) by Wayne D. Blizard.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'signed_multiset'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install signed_multiset
34
+
35
+ ## Usage
36
+
37
+ ```ruby
38
+ require 'signed_multiset'
39
+
40
+ s = SignedMultiset.new(a: 1, b: 2, c: 3)
41
+ #=> => <SignedMultiset a: 1, b: 2, c: 3>
42
+
43
+ s[:b]
44
+ #=> 2
45
+
46
+ s[:d] = -4
47
+ #=> -4
48
+
49
+ s << :d
50
+ #=> <SignedMultiset a: 1, b: 2, c: 3, d: -3>
51
+
52
+ s.increment(a, -1)
53
+ #=> 0
54
+
55
+ s[:a]
56
+ #=> nil
57
+ ```
58
+
59
+ ## Contributing
60
+
61
+ 1. Fork it
62
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
63
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
64
+ 4. Push to the branch (`git push origin my-new-feature`)
65
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/**/*_test.rb"
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: [:test]
@@ -0,0 +1,2 @@
1
+ require "signed_multiset/version"
2
+ require "signed_multiset/signed_multiset"
@@ -0,0 +1,198 @@
1
+ class SignedMultiset
2
+
3
+ include Enumerable
4
+ include Comparable
5
+
6
+ # Create a new instance with a list of objects.
7
+ #
8
+ # @param *list [Object] A list of objects to add to the set
9
+ def self.[](*list)
10
+ new(list)
11
+ end
12
+
13
+ # Create a new SignedMultiset instance.
14
+ #
15
+ # @param object [Enumerable, nil] An array of keys, or key-muliplicity pairs.
16
+ def initialize(object=nil)
17
+ if object.is_a?(Enumerable)
18
+ object.each { |k, v| increment(k, v || 1) }
19
+ end
20
+ end
21
+
22
+ # Get the non-zero key-multiplicity pairs.
23
+ #
24
+ # @return [Hash]
25
+ def multiplicities
26
+ entries.delete_if{|k,v| v == 0}
27
+ end
28
+
29
+ # Iterate over the multiplicity collection.
30
+ #
31
+ # @return [Enumerator]
32
+ def each(*args, &block)
33
+ if block_given?
34
+ multiplicities.each { |k,v| yield(k,v) }
35
+ else
36
+ multiplicities.each(args)
37
+ end
38
+ end
39
+
40
+ # Get the multiplicity for a key.
41
+ #
42
+ # @param key [Object] The key to get the multiplicity of
43
+ # @return [Integer, nil] The multiplicity for the key, or nil if the key is
44
+ # not present, or has a zero multiplicity
45
+ def [](key)
46
+ multiplicities[key]
47
+ end
48
+
49
+ # Set the multiplicity for a key.
50
+ #
51
+ # @param key [Object] The key to set the multiplicity of
52
+ # @param multiplicity [Integer] The desired multiplicity
53
+ # @return [Integer] The multiplicity for the key
54
+ def []=(key, multiplicity)
55
+ entries[key] = multiplicity
56
+ multiplicities[key]
57
+ end
58
+
59
+ # Increment the multiplicity for a key.
60
+ #
61
+ # @param key (see #[]=)
62
+ # @param value [Integer] The desired increment value, positive or negative
63
+ # @return (see #[]=)
64
+ def increment(key, value)
65
+ entries[key] ||= 0
66
+ entries[key] += value
67
+ end
68
+
69
+ # Increment multiplicity by 1 for a key. This method is chainable.
70
+ #
71
+ # @param key [Object] The key to increment the multiplicity of
72
+ # @return [self]
73
+ def <<(key)
74
+ increment(key, 1)
75
+ self
76
+ end
77
+
78
+ # Creates a new instance of equal to current instance
79
+ # @return [SignedMultiset]
80
+ def dup
81
+ self.class.new(multiplicities)
82
+ end
83
+
84
+ # Combine self with another SignedMultiset via addition to create a merged instance.
85
+ #
86
+ # @param other [SignedMultiset]
87
+ # @return [SignedMultiset]
88
+ def +(other)
89
+ other.multiplicities.reduce(self.dup) do |m, (k, v)|
90
+ m.increment(k,v); m
91
+ end
92
+ end
93
+
94
+ # Combine self with another SignedMultiset via subtraction to create a merged instance.
95
+ #
96
+ # @param other (see #+)
97
+ # @return (see #+)
98
+ def -(other)
99
+ other.multiplicities.reduce(self.dup) do |m, (k, v)|
100
+ m.increment(k,-v); m
101
+ end
102
+ end
103
+
104
+ # Combine self with another SignedMultiset via union to create a merged instance.
105
+ #
106
+ # @param other (see #+)
107
+ # @return (see #+)
108
+ def |(other)
109
+ (keys | other.keys).reduce(self.class.new) do |m, k|
110
+ m.increment(k, [self[k] || 0, other[k] || 0].max); m
111
+ end
112
+ end
113
+
114
+ # Combine self with another SignedMultiset via intersection to create a merged instance.
115
+ #
116
+ # @param other (see #+)
117
+ # @return (see #+)
118
+ def &(other)
119
+ (keys & other.keys).reduce(self.class.new) do |m, k|
120
+ value = [self[k], other[k]].min
121
+ m.increment(k, value); m
122
+ end
123
+ end
124
+
125
+ # Get the list of keys for self.
126
+ #
127
+ # @return [Array]
128
+ def keys
129
+ multiplicities.keys
130
+ end
131
+
132
+ # Get the multiplicity values for self.
133
+ #
134
+ # @return [Array]
135
+ def values
136
+ multiplicities.values
137
+ end
138
+
139
+ # Get the cardinality (sum of multiplicities) for self.
140
+ #
141
+ # @return [Integer]
142
+ def cardinality
143
+ values.inject(&:+)
144
+ end
145
+ alias_method :sum, :cardinality
146
+
147
+ # Get the count of unique keys in the SignedMultiset.
148
+ #
149
+ # @return [Integer]
150
+ def size
151
+ keys.size
152
+ end
153
+ alias_method :length, :size
154
+
155
+ # Compare self with another SignedMultiset
156
+ #
157
+ # @param other (see #+)
158
+ # @return [-1,0,1]
159
+ def <=>(other)
160
+ if [:multiplicities, :cardinality, :size].all? { |m| other.respond_to?(m) }
161
+ if multiplicities == other.multiplicities
162
+ 0
163
+ elsif cardinality != other.cardinality
164
+ cardinality <=> other.cardinality
165
+ elsif size != other.size
166
+ size <=> other.size
167
+ else
168
+ multiplicities <=> other.multiplicities
169
+ end
170
+ end
171
+ end
172
+
173
+ def to_hash
174
+ multiplicities.dup
175
+ end
176
+
177
+ def to_a
178
+ multiplicities.to_a
179
+ end
180
+
181
+ def to_s
182
+ multiplicities.map{ |k,c| "#{k}: #{c}"}.join(', ')
183
+ end
184
+
185
+ def inspect
186
+ "<SignedMultiset #{to_s}>"
187
+ end
188
+
189
+ private
190
+
191
+ # Get the key-multiplicity pairs (even those with zero multiplicities).
192
+ #
193
+ # @return [Hash]
194
+ def entries
195
+ @entries ||= {}
196
+ end
197
+
198
+ end
@@ -0,0 +1,3 @@
1
+ class SignedMultiset
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'signed_multiset/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "signed_multiset"
8
+ spec.version = SignedMultiset::VERSION
9
+ spec.authors = ["Josh Lewis"]
10
+ spec.email = ["josh.w.lewis@gmail.com"]
11
+ spec.description = %q{Multisets with negative membership}
12
+ spec.summary = %q{Signed Multiset is a Ruby implementation of a Multiset that allows negative membership. You can think of it as a Multiset or Bag that allows for negative counts. It feels like a Ruby Hash or Array, but with some differences.}
13
+ spec.homepage = "http://github.com/joshwlewis/signed_multiset"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+
25
+ end
@@ -0,0 +1,196 @@
1
+ require 'test_helper'
2
+
3
+ describe SignedMultiset do
4
+ subject { SignedMultiset.new(foo: 4, bar: 2, baz: 0) }
5
+ let(:other){ SignedMultiset.new(foo: -3, qux: 2) }
6
+
7
+ describe "#initialize" do
8
+ it "must accept a hash" do
9
+ subject.keys.must_equal([:foo, :bar])
10
+ end
11
+ it "must accept no arguments" do
12
+ set = SignedMultiset.new
13
+ set.keys.must_equal([])
14
+ end
15
+ it "must accept an Array" do
16
+ set = SignedMultiset.new([:a, :b, :c, :a, :b])
17
+ set.keys.must_equal([:a, :b, :c])
18
+ end
19
+ it "must accept a SignedMultiset" do
20
+ set = SignedMultiset.new(subject)
21
+ set.keys.must_equal([:foo, :bar])
22
+ end
23
+ end
24
+
25
+ describe "::[]" do
26
+ it "must create a new set" do
27
+ set = SignedMultiset[:a, :b, :b, :c]
28
+ set.keys.must_equal([:a, :b, :c])
29
+ end
30
+ end
31
+
32
+ describe "multiplicities" do
33
+ it "must be an Enumerable" do
34
+ subject.multiplicities.must_be_kind_of(Enumerable)
35
+ end
36
+ it "should delete keys with zero multiplicitys" do
37
+ subject.multiplicities.keys.must_equal([:foo, :bar])
38
+ end
39
+ end
40
+
41
+ describe "#[]" do
42
+ it "must find existing keys and return multiplicity" do
43
+ subject[:foo].must_equal(4)
44
+ subject[:bar].must_equal(2)
45
+ end
46
+ it "must return nil for missing keys" do
47
+ subject[:qux].must_be_nil
48
+ end
49
+ it "must return nil for keys with zero multiplicitys" do
50
+ subject[:bax].must_be_nil
51
+ end
52
+ end
53
+
54
+ describe "#[]=" do
55
+ it "must set multiplicity for exisiting keys" do
56
+ subject[:foo].must_equal(4)
57
+ subject[:foo] = -1
58
+ subject[:foo].must_equal(-1)
59
+ end
60
+
61
+ it "must increment key and set multiplicity for new keys" do
62
+ subject[:baz].must_be_nil
63
+ subject[:baz] = 3
64
+ subject[:baz].must_equal(3)
65
+ end
66
+ end
67
+
68
+ describe "increment" do
69
+ it "should increment existing keys" do
70
+ subject[:bar].must_equal(2)
71
+ subject.increment(:bar, -4)
72
+ subject[:bar].must_equal(-2)
73
+ end
74
+ it "should increment new keys" do
75
+ subject[:qux].must_be_nil
76
+ subject.increment(:qux, 2)
77
+ subject[:qux].must_equal(2)
78
+ end
79
+ end
80
+
81
+ describe "<<" do
82
+ it "should increment existing keys" do
83
+ subject[:bar].must_equal(2)
84
+ subject << :bar
85
+ subject[:bar].must_equal(3)
86
+ end
87
+ it "should increment new keys" do
88
+ subject[:qux].must_be_nil
89
+ subject << :qux
90
+ subject[:qux].must_equal(1)
91
+ end
92
+ it "should be chainable" do
93
+ subject << :foo << :baz << :foo
94
+ subject[:foo].must_equal(6)
95
+ subject[:baz].must_equal(1)
96
+ end
97
+ end
98
+
99
+ describe "#+" do
100
+ let(:merged) { subject + other }
101
+ it "should return a new set" do
102
+ merged.must_be_instance_of(SignedMultiset)
103
+ merged.wont_equal(subject)
104
+ merged.wont_equal(other)
105
+ end
106
+ it "should sum the keys" do
107
+ merged[:foo].must_equal(1)
108
+ merged[:bar].must_equal(2)
109
+ merged[:qux].must_equal(2)
110
+ end
111
+ end
112
+
113
+ describe "#-" do
114
+ let(:merged) { subject - other }
115
+ it "should return a new set" do
116
+ merged.must_be_instance_of(SignedMultiset)
117
+ merged.wont_equal(subject)
118
+ merged.wont_equal(other)
119
+ end
120
+ it "should sum the keys" do
121
+ merged[:foo].must_equal(7)
122
+ merged[:bar].must_equal(2)
123
+ merged[:qux].must_equal(-2)
124
+ end
125
+ end
126
+
127
+ describe "#&" do
128
+ let(:merged) { subject & other }
129
+ it "should reutrn a new set" do
130
+ subject.must_be_instance_of(SignedMultiset)
131
+ merged.wont_equal(subject)
132
+ merged.wont_equal(other)
133
+ end
134
+ it "should return the intersection" do
135
+ merged[:foo].must_equal(-3)
136
+ merged[:bar].must_be_nil
137
+ merged[:baz].must_be_nil
138
+ merged[:qux].must_be_nil
139
+ end
140
+ end
141
+
142
+ describe "#|" do
143
+ let(:merged) { subject | other }
144
+ it "should reutrn a new set" do
145
+ subject.must_be_instance_of(SignedMultiset)
146
+ merged.wont_equal(subject)
147
+ merged.wont_equal(other)
148
+ end
149
+ it "should return the union" do
150
+ merged[:foo].must_equal(4)
151
+ merged[:bar].must_equal(2)
152
+ merged[:baz].must_be_nil
153
+ merged[:qux].must_equal(2)
154
+ end
155
+ end
156
+
157
+ describe "#to_hash" do
158
+ it "should return a hash" do
159
+ subject.to_hash.must_equal(foo: 4, bar: 2)
160
+ end
161
+ end
162
+
163
+ describe "#to_a" do
164
+ it "should return an Array" do
165
+ subject.to_a.must_equal([[:foo, 4], [:bar, 2]])
166
+ end
167
+ end
168
+
169
+ describe "#cardinality" do
170
+ it "should return the total of all multiplicitys" do
171
+ subject.cardinality.must_equal(6)
172
+ end
173
+ end
174
+
175
+ describe "#size" do
176
+ it "should return the multiplicity of non-zero keys" do
177
+ subject.size.must_equal(2)
178
+ end
179
+ end
180
+
181
+ describe "#<=>" do
182
+ let(:small) { SignedMultiset[:foo, :bar] }
183
+ let(:large) { SignedMultiset[:foo, :bar, :baz, :foo, :bar, :qux, :foo, :bar] }
184
+ let(:equal) { subject.dup }
185
+
186
+ it "should return 1 when compared to a smaller set" do
187
+ (subject <=> small).must_equal(1)
188
+ end
189
+ it "should return -1 when compared to a larger set" do
190
+ (subject <=> large).must_equal(-1)
191
+ end
192
+ it "should return 0 when compared to an equal set" do
193
+ (subject <=> equal).must_equal(0)
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,4 @@
1
+ require 'signed_multiset'
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: signed_multiset
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josh Lewis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Multisets with negative membership
56
+ email:
57
+ - josh.w.lewis@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .ruby-version
64
+ - .travis.yml
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/signed_multiset.rb
70
+ - lib/signed_multiset/signed_multiset.rb
71
+ - lib/signed_multiset/version.rb
72
+ - signed_multiset.gemspec
73
+ - test/signed_multiset/signed_multiset_test.rb
74
+ - test/test_helper.rb
75
+ homepage: http://github.com/joshwlewis/signed_multiset
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.0.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Signed Multiset is a Ruby implementation of a Multiset that allows negative
99
+ membership. You can think of it as a Multiset or Bag that allows for negative counts.
100
+ It feels like a Ruby Hash or Array, but with some differences.
101
+ test_files:
102
+ - test/signed_multiset/signed_multiset_test.rb
103
+ - test/test_helper.rb
104
+ has_rdoc: