vector_number 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,45 +3,105 @@
3
3
  class VectorNumber
4
4
  # Various mathematical operations that are also conversions.
5
5
  module MathConverting
6
- # Return the absolute value of the vector, i.e. its length.
6
+ # Calculate the absolute value of the vector, i.e. its length.
7
+ #
8
+ # @example
9
+ # VectorNumber[5.3].abs # => 5.3
10
+ # VectorNumber[-5.3i].magnitude # => 5.3
11
+ # VectorNumber[-5.3i, "i"].abs # => 5.3935146240647205
12
+ #
7
13
  # @return [Float]
14
+ #
15
+ # @since 0.2.2
8
16
  def abs
9
- Math.sqrt(coefficients.sum(&:abs2)) # rubocop:disable Naming/VariableNumber
17
+ Math.sqrt(abs2)
10
18
  end
11
19
 
12
20
  alias magnitude abs
13
21
 
14
- # Return the square of absolute value.
22
+ # Calculate the square of absolute value.
23
+ #
24
+ # @example
25
+ # VectorNumber[5.3].abs2 # => 5.3
26
+ # VectorNumber[-5.3i].abs2 # => 5.3
27
+ # VectorNumber[-5.3i, "i"].abs2 # => 29.09
28
+ #
15
29
  # @return [Float]
16
- def abs2 # rubocop:disable Naming/VariableNumber
17
- abs**2
30
+ #
31
+ # @since 0.2.2
32
+ def abs2
33
+ coefficients.sum(&:abs2)
18
34
  end
19
35
 
20
36
  # Return a new vector with every coefficient truncated using their +#truncate+.
37
+ #
38
+ # @example
39
+ # VectorNumber[5.39].truncate # => (5)
40
+ # VectorNumber[-5.35i].truncate # => (-5i)
41
+ # VectorNumber[-5.35i, "i"].truncate # => (-5i + 1⋅'i')
42
+ # VectorNumber[-5.35i, "i"].truncate(1) # => (-5.3i + 1⋅'i')
43
+ # VectorNumber[-5.35i, "i"].truncate(-1) # => (0)
44
+ #
21
45
  # @param digits [Integer]
22
46
  # @return [VectorNumber]
47
+ #
48
+ # @since 0.2.1
23
49
  def truncate(digits = 0)
24
50
  new { _1.truncate(digits) }
25
51
  end
26
52
 
27
53
  # Return a new vector with every coefficient rounded using their +#ceil+.
54
+ #
55
+ # @example
56
+ # VectorNumber[5.39].ceil # => (6)
57
+ # VectorNumber[-5.35i].ceil # => (-5i)
58
+ # VectorNumber[-5.35i, "i"].ceil # => (-5i + 1⋅'i')
59
+ # VectorNumber[-5.35i, "i"].ceil(1) # => (-5.3i + 1⋅'i')
60
+ # VectorNumber[-5.35i, "i"].ceil(-1) # => (10⋅'i')
61
+ #
28
62
  # @param digits [Integer]
29
63
  # @return [VectorNumber]
64
+ #
65
+ # @since 0.2.2
30
66
  def ceil(digits = 0)
31
67
  new { _1.ceil(digits) }
32
68
  end
33
69
 
34
70
  # Return a new vector with every coefficient rounded using their +#floor+.
71
+ #
72
+ # @example
73
+ # VectorNumber[5.39].floor # => (5)
74
+ # VectorNumber[-5.35i].floor # => (-6i)
75
+ # VectorNumber[-5.35i, "i"].floor # => (-6i + 1⋅'i')
76
+ # VectorNumber[-5.35i, "i"].floor(1) # => (-5.4i + 1⋅'i')
77
+ # VectorNumber[-5.35i, "i"].floor(-1) # => (-10i)
78
+ #
35
79
  # @param digits [Integer]
36
80
  # @return [VectorNumber]
81
+ #
82
+ # @since 0.2.2
37
83
  def floor(digits = 0)
38
84
  new { _1.floor(digits) }
39
85
  end
40
86
 
41
87
  # Return a new vector with every coefficient rounded using their +#round+.
88
+ #
89
+ # @example
90
+ # VectorNumber[-4.5i, "i"].round(half: :up) # => (-5i + 1⋅'i')
91
+ # VectorNumber[-4.5i, "i"].round(half: :even) # => (-4i + 1⋅'i')
92
+ # VectorNumber[-5.5i, "i"].round(half: :even) # => (-6i + 1⋅'i')
93
+ # VectorNumber[-5.5i, "i"].round(half: :down) # => (-5i + 1⋅'i')
94
+ # VectorNumber[-5.35i, "i"].round(1) # => (-5.4i + 1⋅'i')
95
+ # VectorNumber[-5.35i, "i"].round(-1) # => (-10i)
96
+ #
42
97
  # @param digits [Integer]
43
- # @param half [Symbol, nil] one of +:up+, +:down+ or +:even+, see +Float#round+ for meaning
98
+ # @param half [Symbol, nil] one of +:up+, +:down+ or +:even+,
99
+ # see +Float#round+ for meaning
44
100
  # @return [VectorNumber]
101
+ #
102
+ # @see Float#round
103
+ #
104
+ # @since 0.2.2
45
105
  def round(digits = 0, half: :up)
46
106
  if defined?(BigDecimal)
47
107
  bd_mode =
@@ -2,11 +2,26 @@
2
2
 
3
3
  class VectorNumber
4
4
  # Methods for performing actual math.
5
+ #
6
+ # All operators (like +*+) have aliases (like +mult+)
7
+ # to make method chaining easier and more natural.
5
8
  module Mathing
6
9
  # The coerce method provides support for Ruby type coercion.
7
- # Unlike most numeric types, VectorNumber can coerce *anything*.
10
+ #
11
+ # Unlike other numeric types, VectorNumber can coerce *anything*.
12
+ #
13
+ # @example
14
+ # VectorNumber["a"].coerce(5) # => [(5), (1⋅'a')]
15
+ # VectorNumber[7].coerce([]) # => [(1⋅[]), (7)]
16
+ # VectorNumber["a"] + 5 # => (1⋅'a' + 5)
17
+ # # Direct reverse coercion doesn't work, but Numeric types know how to call #coerce:
18
+ # 5.coerce(VectorNumber["a"]) # RangeError
19
+ # 5 + VectorNumber["a"] # => (5 + 1⋅'a')
20
+ #
8
21
  # @param other [Object]
9
22
  # @return [Array(VectorNumber, VectorNumber)]
23
+ #
24
+ # @since 0.2.0
10
25
  def coerce(other)
11
26
  case other
12
27
  when VectorNumber
@@ -16,40 +31,87 @@ class VectorNumber
16
31
  end
17
32
  end
18
33
 
19
- # Return self.
20
- # @return [VectorNumber]
21
- def +@
22
- self
23
- end
24
-
25
- # Return new vector with negated coefficients.
26
- # This preserves order of units.
34
+ # Return new vector with negated coefficients (additive inverse).
35
+ #
36
+ # @example
37
+ # -VectorNumber[12, "i"] # => (-12 - 1⋅'i')
38
+ # VectorNumber["a", "b", "a"].neg # => (-2⋅'a' - 1⋅'b')
39
+ # -VectorNumber["a"] + VectorNumber["a"] # => (0)
40
+ #
27
41
  # @return [VectorNumber]
42
+ #
43
+ # @since 0.2.0
28
44
  def -@
29
45
  new(&:-@)
30
46
  end
31
47
 
32
- # Return new vector as a sum of this and other value.
48
+ # @since 0.3.0
49
+ alias neg -@
50
+
51
+ # Return new vector as a sum of this and +other+ value.
33
52
  # This is analogous to {VectorNumber.[]}.
53
+ #
54
+ # @example
55
+ # VectorNumber[5] + 10 # => (15)
56
+ # VectorNumber["a"].add(VectorNumber["b"]) # => (1⋅'a' + 1⋅'b')
57
+ # VectorNumber["a"] + "b" # => (1⋅'a' + 1⋅'b')
58
+ # @example numeric types can be added in reverse
59
+ # 10 + VectorNumber[5] # => (15)
60
+ # 10 + VectorNumber["a"] # => (10 + 1⋅'a')
61
+ #
34
62
  # @param other [Object]
35
63
  # @return [VectorNumber]
64
+ #
65
+ # @since 0.2.0
36
66
  def +(other)
37
67
  new([self, other])
38
68
  end
39
69
 
40
- # Return new vector as a sum of this and negative of the other value.
41
- # This is analogous to {VectorNumber.[]}, but allows to negate anything.
70
+ # @since 0.3.0
71
+ alias add +
72
+
73
+ # Return new vector as a sum of this and additive inverse of +other+ value.
74
+ #
75
+ # This is implemented through {#+} and {#-@}.
76
+ #
77
+ # @example
78
+ # VectorNumber[5] - 3 # => (2)
79
+ # VectorNumber["a"].sub(VectorNumber["b"]) # => (1⋅'a' - 1⋅'b')
80
+ # VectorNumber["a"] - "b" # => (1⋅'a' - 1⋅'b')
81
+ # @example numeric types can be subtracted in reverse
82
+ # 3 - VectorNumber[5] # => (-2)
83
+ # 3 - VectorNumber["a"] # => (3 - 1⋅'a')
84
+ #
42
85
  # @param other [Object]
43
86
  # @return [VectorNumber]
87
+ #
88
+ # @since 0.2.0
44
89
  def -(other)
45
90
  self + new([other], &:-@)
46
91
  end
47
92
 
48
- # Multiply all coefficients by a real number, returning new vector.
49
- # This effectively multiplies magnitude by the specified factor.
93
+ # @since 0.3.0
94
+ alias sub -
95
+
96
+ # Multiply all coefficients by a real +other+, returning new vector.
97
+ #
98
+ # This effectively multiplies {#magnitude} by +other+.
99
+ #
100
+ # @example
101
+ # VectorNumber[5] * 2 # => (10)
102
+ # VectorNumber["a", "b", 6].mult(2) # => (2⋅'a' + 2⋅'b' + 12)
103
+ # VectorNumber["a"] * VectorNumber[2] # => (2⋅'a')
104
+ # # Can't multiply by a non-real:
105
+ # VectorNumber["a"] * VectorNumber["b"] # RangeError
106
+ # @example numeric types can be multiplied in reverse
107
+ # 2 * VectorNumber[5] # => (10)
108
+ # 2 * VectorNumber["a"] # => (2⋅'a')
109
+ #
50
110
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
51
111
  # @return [VectorNumber]
52
112
  # @raise [RangeError] if +other+ is not a number or +other+ can't be multiplied by this one
113
+ #
114
+ # @since 0.2.1
53
115
  def *(other)
54
116
  if real_number?(other)
55
117
  other = other.real
@@ -63,30 +125,67 @@ class VectorNumber
63
125
  end
64
126
  end
65
127
 
66
- # Divide all coefficients by a real number, returning new vector.
67
- # This effectively multiplies magnitude by reciprocal of +other+.
128
+ # @since 0.3.0
129
+ alias mult *
130
+
131
+ # Divide all coefficients by a real +other+, returning new vector.
132
+ #
133
+ # This effectively multiplies {#magnitude} by reciprocal of +other+.
134
+ # @note This method never does integer division.
135
+ #
136
+ # @example
137
+ # VectorNumber[10] / 2 # => (5)
138
+ # VectorNumber["a", "b", 6].quo(2) # => (1/2⋅'a' + 1/2⋅'b' + 3/1)
139
+ # VectorNumber["a"] / VectorNumber[2] # => (1/2⋅'a')
140
+ # # Can't divide by a non-real:
141
+ # VectorNumber["a"] / VectorNumber["b"] # RangeError
142
+ # @example numeric types can be divided in reverse
143
+ # 2 / VectorNumber[10] # => (1/5)
144
+ # # Can't divide by a non-real:
145
+ # 2 / VectorNumber["a"] # RangeError
146
+ #
68
147
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
69
148
  # @return [VectorNumber]
70
149
  # @raise [RangeError] if +other+ is not a number or is not a real number
71
150
  # @raise [ZeroDivisionError] if +other+ is zero
151
+ #
152
+ # @since 0.2.1
72
153
  def /(other)
73
154
  check_divisibility(other)
74
155
 
75
156
  other = other.real
76
157
  # Prevent integer division, but without loss of accuracy.
77
- other = Rational(other) if other.is_a?(Integer)
158
+ other = Rational(other) if other.integer?
78
159
  # @type var other: Float
79
160
  new { _1 / other }
80
161
  end
81
162
 
163
+ # @since 0.2.6
82
164
  alias quo /
165
+ # to fix syntax highlighting: /
83
166
 
84
- # Divide all coefficients by a real number using +fdiv+, returning new vector
85
- # with Float coefficients.
167
+ # Divide all coefficients by a real +other+ using +fdiv+,
168
+ # returning new vector with Float (or BigDecimal) coefficients.
169
+ #
170
+ # There isn't much benefit to this method, as {#/} doesn't do integer division,
171
+ # but it is provided for consistency.
172
+ #
173
+ # @example
174
+ # VectorNumber[10].fdiv(2) # => (5.0)
175
+ # VectorNumber["a", "b", 6].fdiv(2) # => (0.5⋅'a' + 0.5⋅'b' + 3.0)
176
+ # VectorNumber["a"].fdiv(VectorNumber[2]) # => (0.5⋅'a')
177
+ # # Can't divide by a non-real:
178
+ # VectorNumber["a"].fdiv(VectorNumber["b"]) # RangeError
179
+ # @example reverse division may return non-vector results
180
+ # 2.fdiv(VectorNumber[10]) # => 0.2 (Float)
181
+ # 2.0.fdiv(VectorNumber[10]) # => (0.2) (VectorNumber)
182
+ #
86
183
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
87
184
  # @return [VectorNumber]
88
185
  # @raise [RangeError] if +other+ is not a number or is not a real number
89
186
  # @raise [ZeroDivisionError] if +other+ is zero
187
+ #
188
+ # @since 0.2.1
90
189
  def fdiv(other)
91
190
  check_divisibility(other)
92
191
 
@@ -94,14 +193,30 @@ class VectorNumber
94
193
  new { _1.fdiv(other) }
95
194
  end
96
195
 
97
- # Divide all coefficients by +other+, rounding results with {#floor}.
196
+ # Divide all coefficients by a real +other+, rounding results with +#floor+.
197
+ #
98
198
  # This is requal to +(self / other).floor+.
199
+ #
200
+ # @example
201
+ # VectorNumber[10].div(3) # => (3)
202
+ # VectorNumber["a"].div(2) # => (0⋅'a')
203
+ # VectorNumber["a"].div(VectorNumber[2]) # => (0⋅'a')
204
+ # # Can't divide by a non-real:
205
+ # VectorNumber["a"].div(VectorNumber["b"]) # RangeError
206
+ # @example numeric types can be divided in reverse
207
+ # 2.div(VectorNumber[10]) # => (0)
208
+ # # Can't divide by a non-real:
209
+ # 2.div(VectorNumber["a"]) # RangeError
210
+ #
211
+ # @see #divmod
212
+ # @see #%
213
+ #
99
214
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
100
215
  # @return [VectorNumber]
101
216
  # @raise [RangeError] if +other+ is not a number or is not a real number
102
217
  # @raise [ZeroDivisionError] if +other+ is zero
103
- # @see #divmod
104
- # @see #%
218
+ #
219
+ # @since 0.2.6
105
220
  def div(other)
106
221
  check_divisibility(other)
107
222
 
@@ -109,16 +224,36 @@ class VectorNumber
109
224
  new { _1.div(other) }
110
225
  end
111
226
 
112
- # Return the modulus of dividing self by +other+ as a vector.
113
- # This is equal to +self - other * (self/other).floor+.
227
+ # Return the modulus of dividing self by a real +other+ as a vector.
228
+ #
229
+ # This is equal to +self - other * (self/other).floor+,
230
+ # or, alternatively, +self - other * self.div(other)+.
231
+ #
232
+ # @example
233
+ # VectorNumber[10] % 3 # => (1)
234
+ # VectorNumber["a", "b", 6].modulo(2) # => (1⋅'a' + 1⋅'b')
235
+ # -VectorNumber["a"] % VectorNumber[2] # => (1⋅'a')
236
+ # # Can't divide by a non-real:
237
+ # VectorNumber["a"] % VectorNumber["b"] # RangeError
238
+ # @example numeric types can be divided in reverse
239
+ # 3 % VectorNumber[10] # => (3)
240
+ # # Can't divide by a non-real:
241
+ # 3 % VectorNumber["a"] # RangeError
242
+ # @example compare to #remainder
243
+ # VectorNumber[-5] % 3 # => (1)
244
+ # VectorNumber[-5].remainder(3) # => (-2)
245
+ #
246
+ # @see #divmod
247
+ # @see #div
248
+ # @see #remainder
249
+ # @see Numeric#%
250
+ #
114
251
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
115
252
  # @return [VectorNumber]
116
253
  # @raise [RangeError] if +other+ is not a number or is not a real number
117
254
  # @raise [ZeroDivisionError] if +other+ is zero
118
- # @see #divmod
119
- # @see #div
120
- # @see #remainder for alternative
121
- # @see Numeric#% for examples
255
+ #
256
+ # @since 0.2.6
122
257
  def %(other)
123
258
  check_divisibility(other)
124
259
 
@@ -126,28 +261,63 @@ class VectorNumber
126
261
  new { _1 % other }
127
262
  end
128
263
 
264
+ # @since 0.2.6
129
265
  alias modulo %
130
266
 
131
- # Return the quotient and modulus of dividing self by +other+.
267
+ # Return the quotient and modulus of dividing self by a real +other+.
132
268
  # There is no performance benefit compared to calling {#div} and {#%} separately.
269
+ #
270
+ # @example
271
+ # VectorNumber[10].divmod(3) # => [(3), (1)]
272
+ # VectorNumber["a"].divmod(2) # => [(0⋅'a'), (1⋅'a')]
273
+ # VectorNumber["a"].divmod(VectorNumber[2]) # => [(0⋅'a'), (1⋅'a')]
274
+ # # Can't divide by a non-real:
275
+ # VectorNumber["a"].divmod(VectorNumber["b"]) # RangeError
276
+ # @example numeric types can be divided in reverse
277
+ # 3.divmod(VectorNumber[10]) # => [(0), (3)]
278
+ # # Can't divide by a non-real:
279
+ # 3.divmod(VectorNumber["a"]) # RangeError
280
+ #
281
+ # @see #div
282
+ # @see #%
283
+ #
133
284
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
134
285
  # @return [Array(VectorNumber, VectorNumber)]
135
286
  # @raise [RangeError] if +other+ is not a number or is not a real number
136
287
  # @raise [ZeroDivisionError] if +other+ is zero
137
- # @see #div
138
- # @see #%
288
+ #
289
+ # @since 0.2.6
139
290
  def divmod(other)
140
291
  [div(other), modulo(other)]
141
292
  end
142
293
 
143
- # Return the remainder of dividing self by +other+ as a vector.
294
+ # Return the remainder of dividing self by a real +other+ as a vector.
295
+ #
144
296
  # This is equal to +self - other * (self/other).truncate+.
297
+ #
298
+ # @example
299
+ # VectorNumber[10].remainder(3) # => (1)
300
+ # VectorNumber["a"].remainder(2) # => (1⋅'a')
301
+ # -VectorNumber["a"].remainder(VectorNumber[2]) # => (-1⋅'a')
302
+ # # Can't divide by a non-real:
303
+ # VectorNumber["a"].remainder(VectorNumber["b"]) # RangeError
304
+ # @example numeric types can be divided in reverse
305
+ # 3.remainder(VectorNumber[10]) # => (3)
306
+ # # Can't divide by a non-real:
307
+ # 3.remainder(VectorNumber["a"]) # RangeError
308
+ # @example compare to #%
309
+ # VectorNumber[-5] % 3 # => (1)
310
+ # VectorNumber[-5].remainder(3) # => (-2)
311
+ #
312
+ # @see #%
313
+ # @see Numeric#remainder
314
+ #
145
315
  # @param other [Integer, Float, Rational, BigDecimal, VectorNumber]
146
316
  # @return [VectorNumber]
147
317
  # @raise [RangeError] if +other+ is not a number or is not a real number
148
318
  # @raise [ZeroDivisionError] if +other+ is zero
149
- # @see #% for alternative
150
- # @see Numeric#remainder for examples
319
+ #
320
+ # @since 0.2.6
151
321
  def remainder(other)
152
322
  check_divisibility(other)
153
323
 
@@ -157,8 +327,18 @@ class VectorNumber
157
327
 
158
328
  private
159
329
 
330
+ # @param other [Object]
331
+ # @return [void]
332
+ # @raise [RangeError] unless +other+ is a real number
333
+ # @raise [ZeroDivisionError]
334
+ #
335
+ # @see real_number?
336
+ #
337
+ # @since 0.2.6
160
338
  def check_divisibility(other)
161
- raise RangeError, "can't divide #{self} by #{other}", caller unless real_number?(other)
339
+ unless real_number?(other)
340
+ raise RangeError, "can't divide #{self} by #{other.inspect}", caller
341
+ end
162
342
  raise ZeroDivisionError, "divided by 0", caller if other.zero?
163
343
  end
164
344
  end
@@ -1,21 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class VectorNumber
4
- # Refinements of Numeric classes to better work with VectorNumber and similar classes.
4
+ # Refinements of Numeric classes and Kernel to better work with VectorNumber and similar classes.
5
+ #
6
+ # These do not depend on +VectorNumber+ and can technically be used separately.
7
+ # Currently includes:
8
+ # - refinement for +Complex#<=>+ to work with classes implementing +<=>+;
9
+ # - refinement for +Kernel#BigDecimal+ to work with classes implementing +to_d+.
10
+ #
11
+ # @example activating refinements
12
+ # require "vector_number/numeric_refinements"
13
+ # using VectorNumber::NumericRefinements
14
+ #
15
+ # @since 0.2.0
5
16
  module NumericRefinements
6
17
  # Refinement module to provide a +#<=>+ method that can work backwards.
18
+ #
7
19
  # @note Currently only applies to Complex on *3.1*,
8
20
  # as other numeric classes rely on +#coerce+.
21
+ # @example without refinements
22
+ # VectorNumber[2] <=> Complex(1, 0) #=> 1
23
+ # Complex(1, 0) <=> VectorNumber[2] #=> nil
24
+ # @example with refinements
25
+ # require "vector_number/numeric_refinements"
26
+ # using VectorNumber::NumericRefinements
27
+ # VectorNumber[2] <=> Complex(1, 0) #=> 1
28
+ # Complex(1, 0) <=> VectorNumber[2] #=> -1
29
+ #
30
+ # @since 0.2.1
9
31
  module CommutativeShuttle
10
32
  # Commutative +#<=>+.
11
- # @example without refinements
12
- # Complex(1, 0) <=> VectorNumber[2] # => nil
13
- # VectorNumber[2] <=> Complex(1, 0) # => 1
14
- # @example with refinements
15
- # require "vector_number/numeric_refinements"
16
- # using VectorNumber::NumericRefinements
17
- # Complex(1, 0) <=> VectorNumber[2] # => -1
18
- # VectorNumber[2] <=> Complex(1, 0) # => 1
33
+ # Tries to call +other <=> self+ if +self <=> other+ returns +nil+.
19
34
  def <=>(other)
20
35
  comparison = super
21
36
  return comparison if comparison || !other.respond_to?(:<=>)
@@ -30,7 +45,16 @@ class VectorNumber
30
45
  end
31
46
 
32
47
  # Refinement module to change Kernel#BigDecimal so it works with +#to_d+.
48
+ #
33
49
  # @note `BigDecimal` needs to be defined for this refinement to activate.
50
+ # @example without refinements
51
+ # BigDecimal(VectorNumber[2]) # can't convert VectorNumber into BigDecimal (TypeError)
52
+ # @example with refinements
53
+ # require "vector_number/numeric_refinements"
54
+ # using VectorNumber::NumericRefinements
55
+ # BigDecimal(VectorNumber[2]) #=> 0.2e1
56
+ #
57
+ # @since 0.2.1
34
58
  module BigDecimalToD
35
59
  # BigDecimal() that first tries to use #to_d.
36
60
  # @param value [Object]