linguistics 1.0.9 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gemtest +0 -0
  3. data/ChangeLog +849 -342
  4. data/History.rdoc +11 -0
  5. data/LICENSE +9 -9
  6. data/Manifest.txt +44 -0
  7. data/README.rdoc +226 -0
  8. data/Rakefile +32 -349
  9. data/examples/endocs.rb +272 -0
  10. data/examples/generalize_sentence.rb +2 -1
  11. data/examples/klingon.rb +22 -0
  12. data/lib/linguistics.rb +130 -292
  13. data/lib/linguistics/en.rb +337 -1628
  14. data/lib/linguistics/en/articles.rb +138 -0
  15. data/lib/linguistics/en/conjugation.rb +2245 -0
  16. data/lib/linguistics/en/conjunctions.rb +202 -0
  17. data/lib/linguistics/en/{infinitive.rb → infinitives.rb} +41 -55
  18. data/lib/linguistics/en/linkparser.rb +41 -49
  19. data/lib/linguistics/en/numbers.rb +483 -0
  20. data/lib/linguistics/en/participles.rb +33 -0
  21. data/lib/linguistics/en/pluralization.rb +810 -0
  22. data/lib/linguistics/en/stemmer.rb +75 -0
  23. data/lib/linguistics/en/titlecase.rb +121 -0
  24. data/lib/linguistics/en/wordnet.rb +63 -97
  25. data/lib/linguistics/inflector.rb +89 -0
  26. data/lib/linguistics/iso639.rb +534 -448
  27. data/lib/linguistics/languagebehavior.rb +36 -0
  28. data/lib/linguistics/monkeypatches.rb +42 -0
  29. data/spec/lib/constants.rb +15 -0
  30. data/spec/lib/helpers.rb +38 -0
  31. data/spec/linguistics/en/articles_spec.rb +797 -0
  32. data/spec/linguistics/en/conjugation_spec.rb +2083 -0
  33. data/spec/linguistics/en/conjunctions_spec.rb +154 -0
  34. data/spec/linguistics/en/infinitives_spec.rb +518 -0
  35. data/spec/linguistics/en/linkparser_spec.rb +66 -0
  36. data/spec/linguistics/en/numbers_spec.rb +1295 -0
  37. data/spec/linguistics/en/participles_spec.rb +55 -0
  38. data/spec/linguistics/en/pluralization_spec.rb +4636 -0
  39. data/spec/linguistics/en/stemmer_spec.rb +72 -0
  40. data/spec/linguistics/en/titlecase_spec.rb +841 -0
  41. data/spec/linguistics/en/wordnet_spec.rb +85 -0
  42. data/spec/linguistics/en_spec.rb +45 -167
  43. data/spec/linguistics/inflector_spec.rb +40 -0
  44. data/spec/linguistics/iso639_spec.rb +49 -53
  45. data/spec/linguistics/monkeypatches_spec.rb +40 -0
  46. data/spec/linguistics_spec.rb +46 -76
  47. metadata +241 -113
  48. metadata.gz.sig +0 -0
  49. data/README +0 -166
  50. data/README.english +0 -245
  51. data/rake/191_compat.rb +0 -26
  52. data/rake/dependencies.rb +0 -76
  53. data/rake/documentation.rb +0 -123
  54. data/rake/helpers.rb +0 -502
  55. data/rake/hg.rb +0 -318
  56. data/rake/manual.rb +0 -787
  57. data/rake/packaging.rb +0 -129
  58. data/rake/publishing.rb +0 -341
  59. data/rake/style.rb +0 -62
  60. data/rake/svn.rb +0 -668
  61. data/rake/testing.rb +0 -152
  62. data/rake/verifytask.rb +0 -64
  63. data/tests/en/infinitive.tests.rb +0 -207
  64. data/tests/en/inflect.tests.rb +0 -1389
  65. data/tests/en/lafcadio.tests.rb +0 -77
  66. data/tests/en/linkparser.tests.rb +0 -42
  67. data/tests/en/lprintf.tests.rb +0 -77
  68. data/tests/en/titlecase.tests.rb +0 -73
  69. data/tests/en/wordnet.tests.rb +0 -95
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'linguistics' unless defined?( Linguistics )
4
+ require 'linguistics/en' unless defined?( Linguistics::EN )
5
+
6
+ # Ruby-Stemmer support for the English-language Linguistics module. It
7
+ # requires the Ruby-Stemmer gem to be installed; if it is not
8
+ # installed, calling the functions defined by this file will raise
9
+ # NotImplementedErrors.
10
+ #
11
+ # # Test to be sure the Stemmer gem loaded okay.
12
+ # Linguistics::EN.has_stemmer?
13
+ # # => true
14
+ #
15
+ module Linguistics::EN::Stemmer
16
+
17
+ # Module instance variables -- copied over to the EN module when registered
18
+ @has_stemmer = false
19
+ @stemmer_error = nil
20
+ @stemmer = nil
21
+
22
+ # Load Ruby-Stemmer if possible, saving the error that occurs if anything goes wrong.
23
+ begin
24
+ require 'lingua/stemmer'
25
+ @has_stemmer = true
26
+ rescue LoadError => err
27
+ @stemmer_error = err
28
+ end
29
+
30
+
31
+ # Container for methods intended to extend the EN module as singleton methods.
32
+ module SingletonMethods
33
+
34
+ ### Returns +true+ if Ruby-Stemmer was loaded okay
35
+ def has_stemmer? ; @has_stemmer; end
36
+
37
+ ### If #has_stemmer? returns +false+, this can be called to fetch the
38
+ ### exception which was raised when Ruby-Stemmer was loaded.
39
+ def stemmer_error ; @stemmer_error; end
40
+
41
+ end # module SingletonMethods
42
+ extend SingletonMethods
43
+
44
+
45
+ # Register this module to the list of modules to include
46
+ Linguistics::EN.register_extension( self )
47
+
48
+ #################################################################
49
+ ### M O D U L E M E T H O D S
50
+ #################################################################
51
+
52
+ ### The instance of the Lingua::Stemmer used for all Linguistics Stemmer
53
+ ### functions.
54
+ def self::stemmer
55
+ raise self.stemmer_error unless self.has_stemmer?
56
+ @stemmer ||= Lingua::Stemmer.new
57
+ end
58
+
59
+
60
+ #################################################################
61
+ ### S T E M M E R I N T E R F A C E
62
+ #################################################################
63
+
64
+ ######
65
+ public
66
+ ######
67
+
68
+
69
+ ### Return the stem of the receiving word.
70
+ def stem
71
+ return Linguistics::EN::Stemmer.stemmer.stem( self.obj.to_s )
72
+ end
73
+
74
+ end # module Linguistics::EN::Stemmer
75
+
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'linguistics/en' unless defined?( Linguistics::EN )
4
+
5
+ # Methods for capitalizing a sentence as a title, nouns as proper
6
+ # nouns, and for turning a sentence into its equivalent CamelCaseSentence
7
+ # and vice-versa. It's part of the English-language Linguistics module.
8
+ module Linguistics::EN::TitleCase
9
+
10
+ # Register this module to the list of modules to include
11
+ Linguistics::EN.register_extension( self )
12
+
13
+
14
+ # Exceptions: Indefinite articles
15
+ ARTICLES = %w[a and the]
16
+
17
+ # Exceptions: Prepositions shorter than five letters
18
+ SHORT_PREPOSITIONS = ["amid", "at", "but", "by", "down", "for", "from", "in",
19
+ "into", "like", "near", "of", "off", "on", "onto", "out", "over",
20
+ "past", "save", "with", "till", "to", "unto", "up", "upon", "with"]
21
+
22
+ # Exceptions: Coordinating conjunctions
23
+ COORD_CONJUNCTIONS = %w[and but as]
24
+
25
+ # Titlecase exceptions: "In titles, capitalize the first word, the
26
+ # last word, and all words in between except articles (a, an, and
27
+ # the), prepositions under five letters (in, of, to), and coordinating
28
+ # conjunctions (and, but). These rules apply to titles of long, short,
29
+ # and partial works as well as your own papers" (Anson, Schwegler,
30
+ # and Muth. The Longman Writer's Companion 240).
31
+ TITLE_CASE_EXCEPTIONS = ARTICLES | SHORT_PREPOSITIONS | COORD_CONJUNCTIONS
32
+
33
+ # The words which don't get capitalized in a compound proper noun
34
+ PROPER_NOUN_EXCEPTIONS = %w{and the of}
35
+
36
+
37
+
38
+ ### Turns a camel-case +string+ ("camelCaseToEnglish") to plain English
39
+ ### ("camel case to english"). Each word is decapitalized.
40
+ def un_camel_case
41
+ self.to_s.
42
+ gsub( /([A-Z])([A-Z])/ ) { "#$1 #$2" }.
43
+ gsub( /([a-z])([A-Z])/ ) { "#$1 #$2" }.downcase
44
+ end
45
+
46
+
47
+ ### Turns an English language +string+ into a CamelCase word.
48
+ def to_camel_case
49
+ self.to_s.gsub( /\s+([a-z])/i ) { $1.upcase }
50
+ end
51
+
52
+
53
+ ### Returns the inflected object as a title-cased String.
54
+ ###
55
+ ### Some examples:
56
+ ###
57
+ ### "a portrait of the artist as a young man".en.titlecase
58
+ ### # => "A Portrait of the Artist as a Young Man"
59
+ ###
60
+ ### "a seven-sided romance".en.titlecase
61
+ ### # => "A Seven-Sided Romance"
62
+ ###
63
+ ### "the curious incident of the dog in the night-time".en.titlecase
64
+ ### # => "The Curious Incident of the Dog in the Night-Time"
65
+ ###
66
+ ### "the rats of n.i.m.h.".en.titlecase
67
+ ### # => "The Rats of N.I.M.H."
68
+ def titlecase
69
+
70
+ # Split on word-boundaries
71
+ words = self.to_s.split( /\b/ )
72
+
73
+ # Always capitalize the first and last words
74
+ words.first.capitalize!
75
+ words.last.capitalize!
76
+
77
+ # Now scan the rest of the tokens, skipping non-words and capitalization
78
+ # exceptions.
79
+ words.each_with_index do |word, i|
80
+
81
+ # Non-words
82
+ next unless /^\w+$/.match( word )
83
+
84
+ # Skip exception-words
85
+ next if TITLE_CASE_EXCEPTIONS.include?( word )
86
+
87
+ # Skip second parts of contractions
88
+ next if words[i - 1] == "'" && /\w/.match( words[i - 2] )
89
+
90
+ # Have to do it this way instead of capitalize! because that method
91
+ # also downcases all other letters.
92
+ word.gsub!( /^(\w)(.*)/ ) { $1.upcase + $2 }
93
+ end
94
+
95
+ return words.join
96
+ end
97
+
98
+
99
+ ### Returns the proper noun form of the inflected object by capitalizing most of the
100
+ ### words.
101
+ ###
102
+ ### Some examples:
103
+ ###
104
+ ### "bosnia and herzegovina".en.proper_noun
105
+ ### # => "Bosnia and Herzegovina"
106
+ ### "macedonia, the former yugoslav republic of".en.proper_noun
107
+ ### # => "Macedonia, the Former Yugoslav Republic of"
108
+ ### "virgin islands, u.s.".en.proper_noun
109
+ ### # => "Virgin Islands, U.S."
110
+ def proper_noun
111
+ return self.to_s.split(/([ .]+)/).collect do |word|
112
+ next word unless
113
+ /^[a-z]/.match( word ) &&
114
+ ! (PROPER_NOUN_EXCEPTIONS.include?( word ))
115
+ word.capitalize
116
+ end.join
117
+ end
118
+
119
+
120
+ end # module Linguistics::EN::TitleCase
121
+
@@ -1,61 +1,58 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- require 'linguistics/en'
3
+ require 'linguistics/en' unless defined?( Linguistics::EN )
4
4
 
5
- # This file contains functions for finding relations for English words. It
6
- # requires the Ruby-WordNet module to be installed; if it is not installed,
7
- # calling the functions defined by this file will raise NotImplemented
8
- # exceptions if called. Requiring this file adds functions and constants to the
9
- # Linguistics::EN module.
10
- #
11
- # == Synopsis
5
+ # WordNet support for the English-language Linguistics module. It
6
+ # requires the Ruby-WordNet module to be installed; if it is not
7
+ # installed, calling the functions defined by this file will raise
8
+ # NotImplementedErrors.
12
9
  #
13
10
  # # Test to be sure the WordNet module loaded okay.
14
11
  # Linguistics::EN.has_wordnet?
15
12
  # # => true
16
- #
13
+ #
17
14
  # # Fetch the default synset for the word "balance"
18
- # "balance".synset
15
+ # "balance".en.synset
19
16
  # # => #<WordNet::Synset:0x40376844 balance (noun): "a state of equilibrium"
20
17
  # (derivations: 3, antonyms: 1, hypernyms: 1, hyponyms: 3)>
21
- #
18
+ #
22
19
  # # Fetch the synset for the first verb sense of "balance"
23
20
  # "balance".en.synset( :verb )
24
21
  # # => #<WordNet::Synset:0x4033f448 balance, equilibrate, equilibrize, equilibrise
25
22
  # (verb): "bring into balance or equilibrium; "She has to balance work and her
26
23
  # domestic duties"; "balance the two weights"" (derivations: 7, antonyms: 1,
27
24
  # verbGroups: 2, hypernyms: 1, hyponyms: 5)>
28
- #
25
+ #
29
26
  # # Fetch the second noun sense
30
27
  # "balance".en.synset( 2, :noun )
31
28
  # # => #<WordNet::Synset:0x404ebb24 balance (noun): "a scale for weighing; depends
32
29
  # on pull of gravity" (hypernyms: 1, hyponyms: 5)>
33
- #
30
+ #
34
31
  # # Fetch the second noun sense's hypernyms (more-general words, like a superclass)
35
32
  # "balance".en.synset( 2, :noun ).hypernyms
36
33
  # # => [#<WordNet::Synset:0x404e5620 scale, weighing machine (noun): "a measuring
37
34
  # instrument for weighing; shows amount of mass" (derivations: 2, hypernyms: 1,
38
35
  # hyponyms: 2)>]
39
- #
36
+ #
40
37
  # # A simpler way of doing the same thing:
41
38
  # "balance".en.hypernyms( 2, :noun )
42
39
  # # => [#<WordNet::Synset:0x404e5620 scale, weighing machine (noun): "a measuring
43
40
  # instrument for weighing; shows amount of mass" (derivations: 2, hypernyms: 1,
44
41
  # hyponyms: 2)>]
45
- #
42
+ #
46
43
  # # Fetch the first hypernym's hypernyms
47
44
  # "balance".en.synset( 2, :noun ).hypernyms.first.hypernyms
48
45
  # # => [#<WordNet::Synset:0x404c60b8 measuring instrument, measuring system,
49
46
  # measuring device (noun): "instrument that shows the extent or amount or quantity
50
47
  # or degree of something" (hypernyms: 1, hyponyms: 83)>]
51
- #
48
+ #
52
49
  # # Find the synset to which both the second noun sense of "balance" and the
53
50
  # # default sense of "shovel" belong.
54
51
  # ("balance".en.synset( 2, :noun ) | "shovel".en.synset)
55
52
  # # => #<WordNet::Synset:0x40473da4 instrumentality, instrumentation (noun): "an
56
53
  # artifact (or system of artifacts) that is instrumental in accomplishing some
57
54
  # end" (derivations: 1, hypernyms: 1, hyponyms: 13)>
58
- #
55
+ #
59
56
  # # Fetch just the words for the other kinds of "instruments"
60
57
  # "instrument".en.hyponyms.collect {|synset| synset.words}.flatten
61
58
  # # => ["analyzer", "analyser", "cautery", "cauterant", "drafting instrument",
@@ -65,25 +62,13 @@ require 'linguistics/en'
65
62
  # instrument", "sonograph", "surveying instrument", "surveyor's instrument",
66
63
  # "tracer", "weapon", "arm", "weapon system", "whip"]
67
64
  #
68
- #
69
- # == Authors
70
- #
71
- # * Michael Granger <ged@FaerieMUD.org>
72
- #
73
- # :include: LICENSE
74
- #
75
- # == Version
76
- #
77
- # $Id: wordnet.rb,v 2640c845eb5c 2009/11/17 16:59:25 ged $
78
- #
79
- module Linguistics::EN
65
+ module Linguistics::EN::WordNet
80
66
 
81
- @has_wordnet = false
82
- @wn_error = nil
83
- @wn_lexicon = nil
67
+ @has_wordnet = false
68
+ @wn_error = nil
69
+ @lexicon = nil
84
70
 
85
- # Load WordNet and open the lexicon if possible, saving the error that
86
- # occurs if anything goes wrong.
71
+ # Load WordNet if possible, saving the error that occurs if anything goes wrong.
87
72
  begin
88
73
  require 'wordnet'
89
74
  @has_wordnet = true
@@ -92,44 +77,48 @@ module Linguistics::EN
92
77
  end
93
78
 
94
79
 
95
- #################################################################
96
- ### M O D U L E M E T H O D S
97
- #################################################################
98
- class << self
80
+ # Container for methods intended to extend the EN module as singleton methods.
81
+ module SingletonMethods
99
82
 
100
83
  ### Returns +true+ if WordNet was loaded okay
101
84
  def has_wordnet? ; @has_wordnet; end
102
85
 
103
- ### If #haveWordnet? returns +false+, this can be called to fetch the
86
+ ### If #has_wordnet? returns +false+, this can be called to fetch the
104
87
  ### exception which was raised when WordNet was loaded.
105
- def wn_error ; @wn_error; end
106
-
107
- ### The instance of the WordNet::Lexicon used for all Linguistics WordNet
108
- ### functions.
109
- def wn_lexicon
110
- if @wn_error
111
- raise NotImplementedError,
112
- "WordNet functions are not loaded: %s" %
113
- @wn_error.message
114
- end
115
-
116
- @wn_lexicon ||= WordNet::Lexicon::new
117
- end
88
+ def wordnet_error ; @wn_error; end
89
+
90
+ end # module SingletonMethods
91
+ extend SingletonMethods
118
92
 
119
- ### Make a function that calls the method +meth+ on the synset of an input
120
- ### word.
121
- def def_synset_function( meth )
122
- (class << self; self; end).instance_eval do
123
- define_method( meth ) {|*args|
124
- word, pos, sense = *args
125
- raise ArgumentError,
126
- "wrong number of arguments (0 for 1)" unless word
127
- sense ||= 1
128
-
129
- syn = synset( word.to_s, pos, sense )
130
- return syn.nil? ? nil : syn.send( meth )
131
- }
132
- end
93
+
94
+ # Register this module to the list of modules to include
95
+ Linguistics::EN.register_extension( self )
96
+
97
+
98
+ #################################################################
99
+ ### M O D U L E M E T H O D S
100
+ #################################################################
101
+
102
+ ### The instance of the WordNet::Lexicon used for all Linguistics WordNet
103
+ ### functions.
104
+ def self::lexicon
105
+ raise self.wordnet_error unless self.has_wordnet?
106
+ @lexicon ||= WordNet::Lexicon::new
107
+ end
108
+
109
+
110
+ ### Set the WordNet::Lexicon used by the linguistic functions.
111
+ def self::lexicon=( newlex )
112
+ @lexicon = newlex
113
+ end
114
+
115
+
116
+ ### Make a function that calls the method +meth+ on the synset of an input
117
+ ### word.
118
+ def self::def_synset_function( name )
119
+ define_method( name ) do |*criteria|
120
+ syn = self.synset( *criteria ) or return nil
121
+ return syn.send( name )
133
122
  end
134
123
  end
135
124
 
@@ -139,50 +128,27 @@ module Linguistics::EN
139
128
  ### W O R D N E T I N T E R F A C E
140
129
  #################################################################
141
130
 
142
- ###############
143
- module_function
144
- ###############
131
+ ######
132
+ public
133
+ ######
145
134
 
146
135
  ### Look up the synset associated with the given word or collocation in the
147
136
  ### WordNet lexicon and return a WordNet::Synset object.
148
- def synset( word, pos=nil, sense=1 )
149
- lex = Linguistics::EN::wn_lexicon
150
- if pos.is_a?( Fixnum )
151
- sense = pos
152
- pos = nil
153
- end
154
- postries = pos ? [pos] : [:noun, :verb, :adjective, :adverb, :other]
155
- syn = nil
156
-
157
- postries.each do |pos|
158
- break if syn = lex.lookup_synsets( word.to_s, pos, sense )
159
- end
160
-
161
- return syn
137
+ def synset( *args )
138
+ return Linguistics::EN::WordNet.lexicon[ self.to_s, *args ]
162
139
  end
163
140
 
164
141
 
165
142
  ### Look up all the synsets associated with the given word or collocation in
166
143
  ### the WordNet lexicon and return an Array of WordNet::Synset objects. If
167
144
  ### +pos+ is +nil+, return synsets for all parts of speech.
168
- def synsets( word, pos=nil )
169
- lex = Linguistics::EN::wn_lexicon
170
- postries = pos ? [pos] : [:noun, :verb, :adjective, :adverb, :other]
171
- syns = []
172
-
173
- postries.each {|pos|
174
- syns << lex.lookup_synsets( word.to_s, pos )
175
- }
176
-
177
- return syns.flatten.compact
145
+ def synsets( *args )
146
+ return Linguistics::EN::WordNet.lexicon.lookup_synsets( self.to_s, *args )
178
147
  end
179
148
 
180
149
 
181
150
  # Returns definitions and/or example sentences as a String.
182
- def_synset_function :gloss
183
-
184
- # Returns definitions and/or example sentences as an Array.
185
- def_synset_function :glosses
151
+ def_synset_function :definition
186
152
 
187
153
  # Return nouns or verbs that have the same hypernym as the receiver.
188
154
  def_synset_function :coordinates
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ require 'loggability'
5
+ require 'linguistics' unless defined?( Linguistics )
6
+
7
+ # A facade object that acts as the extension point for linguistic modules
8
+ # for a single language. A single instance of an inflector is generated
9
+ # for an object that has been extended with a Linguistics language
10
+ # the first time the language is used.
11
+ class Linguistics::Inflector
12
+ extend Loggability
13
+
14
+
15
+ # Loggability API -- log to the linguistics logger
16
+ log_to :linguistics
17
+
18
+
19
+ ### Create a new inflector for +obj+.
20
+ def initialize( language_code, obj )
21
+ raise TypeError, "can't inflect for another inflector!" if
22
+ obj.is_a?( Linguistics::Inflector )
23
+ @language_code = language_code
24
+ @obj = obj
25
+ super()
26
+ end
27
+
28
+
29
+ ######
30
+ public
31
+ ######
32
+
33
+ # The object the inflector is delegating for
34
+ attr_reader :obj
35
+
36
+ # The inflector's language code
37
+ attr_reader :language_code
38
+
39
+
40
+ ### Return the english-language name of the language the inflector is delegating
41
+ ### for.
42
+ def language
43
+ ::Linguistics::ISO639::LANGUAGE_CODES[ self.language_code.to_sym ][:eng_name]
44
+ end
45
+
46
+
47
+ ### Returns +true+ if either the inflector or the object it's wrapping respond to
48
+ ### the specified +message+.
49
+ def respond_to_missing?( message, include_priv=false )
50
+ return self.obj.respond_to?( message, include_priv )
51
+ end
52
+
53
+
54
+ ### Return the target object as a String.
55
+ def to_s
56
+ return self.obj.to_s
57
+ end
58
+
59
+
60
+ ### Return the target object as an Integer
61
+ def to_i
62
+ return self.obj.to_i
63
+ end
64
+
65
+
66
+ ### Output a programmer-readable representation of the object suitable for debugging.
67
+ def inspect
68
+ return "#<(%s-language inflector) for <%s:0x%0x> >" % [
69
+ self.language,
70
+ @obj.class,
71
+ @obj.object_id / 2
72
+ ]
73
+ end
74
+
75
+
76
+ #########
77
+ protected
78
+ #########
79
+
80
+ ### Delegate missing methods to the target object.
81
+ def method_missing( sym, *args, &block )
82
+ return super unless self.obj.respond_to?( sym )
83
+ meth = self.obj.method( sym )
84
+ self.singleton_class.send( :define_method, sym, &meth )
85
+ return self.method( sym ).call( *args, &block )
86
+ end
87
+
88
+ end # class Linguistics::Inflector
89
+