opee 0.0.1 → 0.0.2
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.
- data/README.md +33 -2
- data/lib/opee.rb +2 -0
- data/lib/opee/actor.rb +82 -17
- data/lib/opee/env.rb +88 -0
- data/lib/opee/log.rb +43 -0
- data/lib/opee/version.rb +1 -1
- data/test/relay.rb +20 -0
- data/test/tc_opee_actor.rb +78 -0
- data/test/tc_opee_env.rb +22 -0
- data/test/tc_opee_log.rb +21 -0
- data/test/tests.rb +18 -18
- data/test/ts_opee.rb +16 -0
- metadata +10 -3
data/README.md
CHANGED
@@ -7,16 +7,47 @@ push the flow forward, never returning values when using the ask()
|
|
7
7
|
method. Other methods return immediately but they should never modify the data
|
8
8
|
portions of the Actors. They can be used to modify the control of the Actor.
|
9
9
|
|
10
|
-
|
10
|
+
Once a reasonable API has been established a high performance C extension will
|
11
|
+
be written to improve performance.
|
11
12
|
|
12
|
-
|
13
|
+
This is no where close to being ready for prime time.
|
14
|
+
|
15
|
+
# Plans and Notes
|
16
|
+
|
17
|
+
- Env
|
18
|
+
- test
|
19
|
+
- create and close actors and make sure each_actor changes
|
20
|
+
- test logger return Log instance
|
21
|
+
- queue_count
|
22
|
+
- start/stop
|
23
|
+
- wait_close
|
24
|
+
|
25
|
+
- Log
|
26
|
+
- allow forward attribute to be set that forwards messages to another Actor
|
27
|
+
- turn output off (useful when there is a forward actor)
|
28
|
+
- test
|
29
|
+
- set formatter and stringio and test env methods
|
30
|
+
- test forwarding
|
31
|
+
|
32
|
+
- Actor
|
33
|
+
|
34
|
+
- implement a design pattern for a shared work queue
|
35
|
+
- queue is an actor
|
36
|
+
- place items on queue either as normal ask sequence or special mutex protected queue
|
37
|
+
- if normal then wait for a request for a job and send a job to requestor
|
13
38
|
|
14
39
|
- pick a problem to test against
|
40
|
+
- checkers
|
41
|
+
- process some kind of file
|
42
|
+
- process random numbers to produce something
|
43
|
+
- life
|
15
44
|
|
16
45
|
- describe patterns for use
|
17
46
|
|
18
47
|
- Is the notion of a job needed to follow processing of an initial input?
|
19
48
|
- avoid using job for storing data though unless rules can be set up to isolate portions of the data to a specific processing path
|
49
|
+
- need something for sharing large chunks of data
|
50
|
+
- maybe just another actor
|
20
51
|
|
21
52
|
### License:
|
22
53
|
|
data/lib/opee.rb
CHANGED
data/lib/opee/actor.rb
CHANGED
@@ -2,28 +2,56 @@
|
|
2
2
|
module Opee
|
3
3
|
|
4
4
|
class Actor
|
5
|
+
STOPPED = 0
|
6
|
+
RUNNING = 1
|
7
|
+
CLOSING = 2
|
8
|
+
STEP = 3
|
5
9
|
|
6
10
|
def initialize(options={})
|
7
11
|
@queue = []
|
12
|
+
@idle = []
|
13
|
+
@priority = []
|
8
14
|
@ask_mutex = Mutex.new()
|
15
|
+
@priority_mutex = Mutex.new()
|
16
|
+
@idle_mutex = Mutex.new()
|
17
|
+
@step_thread = nil
|
9
18
|
@ask_timeout = 0.0
|
10
19
|
@max_queue_count = nil
|
11
|
-
@
|
20
|
+
@state = RUNNING
|
21
|
+
Env.add_actor(self)
|
12
22
|
set_options(options)
|
13
23
|
@loop = Thread.start(self) do |me|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
while CLOSING != @state
|
25
|
+
begin
|
26
|
+
if RUNNING == @state || STEP == @state
|
27
|
+
a = nil
|
28
|
+
if !@priority.empty?
|
29
|
+
@priority_mutex.synchronize {
|
30
|
+
a = @priority.pop()
|
31
|
+
}
|
32
|
+
elsif !@queue.empty?
|
33
|
+
@ask_mutex.synchronize {
|
34
|
+
a = @queue.pop()
|
35
|
+
}
|
36
|
+
elsif !@idle.empty?
|
37
|
+
@idle_mutex.synchronize {
|
38
|
+
a = @idle.pop()
|
39
|
+
}
|
40
|
+
else
|
41
|
+
Env.wake_finish()
|
42
|
+
sleep(1.0)
|
43
|
+
end
|
44
|
+
send(a.op, *a.args) unless a.nil?
|
45
|
+
if STEP == @state
|
46
|
+
@step_thread.wakeup() unless @step_thread.nil?
|
47
|
+
@state = STOPPED
|
48
|
+
end
|
49
|
+
elsif STOPPED == @state
|
50
|
+
sleep(1.0)
|
51
|
+
end
|
52
|
+
rescue Exception => e
|
53
|
+
Env.log_rescue(e)
|
22
54
|
end
|
23
|
-
rescue Exception => e
|
24
|
-
# TBD handle errors by passing them to a error handler
|
25
|
-
puts "*** #{e.class}: #{e.message}"
|
26
|
-
e.backtrace.each { |line| puts " " + line }
|
27
55
|
end
|
28
56
|
end
|
29
57
|
end
|
@@ -37,22 +65,59 @@ module Opee
|
|
37
65
|
@ask_mutex.synchronize {
|
38
66
|
@queue << Act.new(op, args)
|
39
67
|
}
|
40
|
-
@loop.wakeup() if @
|
68
|
+
@loop.wakeup() if RUNNING == @state
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_idle(op, *args)
|
72
|
+
@idle_mutex.synchronize {
|
73
|
+
@idle << Act.new(op, args)
|
74
|
+
}
|
75
|
+
@loop.wakeup() if RUNNING == @state
|
76
|
+
end
|
77
|
+
|
78
|
+
def priority_ask(op, *args)
|
79
|
+
@priority_mutex.synchronize {
|
80
|
+
@priority << Act.new(op, args)
|
81
|
+
}
|
82
|
+
@loop.wakeup() if RUNNING == @state
|
83
|
+
end
|
84
|
+
|
85
|
+
def method_missing(m, *args, &blk)
|
86
|
+
ask(m, *args)
|
41
87
|
end
|
42
88
|
|
43
89
|
def queue_count()
|
44
|
-
@queue.length
|
90
|
+
@queue.length + @priority.length + @idle.length
|
45
91
|
end
|
46
92
|
|
47
93
|
def stop()
|
48
|
-
@
|
94
|
+
@state = STOPPED
|
95
|
+
end
|
96
|
+
|
97
|
+
def step(max_wait=5)
|
98
|
+
@state = STEP
|
99
|
+
@step_thread = Thread.current
|
100
|
+
@loop.wakeup()
|
101
|
+
sleep(max_wait)
|
49
102
|
end
|
50
103
|
|
51
104
|
def start()
|
52
|
-
@
|
105
|
+
@state = RUNNING
|
53
106
|
@loop.wakeup()
|
54
107
|
end
|
55
108
|
|
109
|
+
def close()
|
110
|
+
@state = CLOSING
|
111
|
+
begin
|
112
|
+
# if the loop has already exited this will raise an Exception that can be ignored
|
113
|
+
@loop.wakeup()
|
114
|
+
rescue
|
115
|
+
# ignore
|
116
|
+
end
|
117
|
+
Env.remove_actor(self)
|
118
|
+
@loop.join()
|
119
|
+
end
|
120
|
+
|
56
121
|
private
|
57
122
|
|
58
123
|
class Act
|
data/lib/opee/env.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
module Opee
|
3
|
+
class Env
|
4
|
+
|
5
|
+
@@actors = []
|
6
|
+
@@log = nil
|
7
|
+
@@finish_thread = nil
|
8
|
+
|
9
|
+
def self.add_actor(actor)
|
10
|
+
@@actors << actor
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.remove_actor(actor)
|
14
|
+
@@actors.delete(actor)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.each_actor(&blk)
|
18
|
+
@@actors.each { |a| blk.yield(a) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.shutdown()
|
22
|
+
until @@actors.empty?
|
23
|
+
a = @@actors.pop()
|
24
|
+
a.close()
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.log(severity, message)
|
29
|
+
@@log = Log.new() if @@log.nil?
|
30
|
+
@@log.ask(:log, severity, message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.log_rescue(ex)
|
34
|
+
@@log = Log.new() if @@log.nil?
|
35
|
+
return unless Logger::Severity::ERROR >= @@log.level
|
36
|
+
msg = "#{ex.class}: #{ex.message}"
|
37
|
+
if Logger::Severity::WARN >= @@log.level
|
38
|
+
ex.backtrace.each { |line| msg << " #{line}\n" }
|
39
|
+
end
|
40
|
+
@@log.ask(:log, Logger::Severity::ERROR, msg)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.logger()
|
44
|
+
@@log = Log.new() if @@log.nil?
|
45
|
+
@@log
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.queue_count()
|
49
|
+
cnt = 0
|
50
|
+
@@actors.each { |a| cnt += a.queue_count() }
|
51
|
+
cnt
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.stop()
|
55
|
+
@@actors.each { |a| a.stop() }
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.start()
|
59
|
+
@@finish_thread = nil
|
60
|
+
@@actors.each { |a| a.start() }
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.wait_finish()
|
64
|
+
@@finish_thread = Thread.current
|
65
|
+
while 0 < queue_count()
|
66
|
+
sleep(0.2) # actors should wake up when queue is empty
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.wake_finish()
|
71
|
+
@@finish_thread.wakeup() unless @@finish_thread.nil?
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.wait_close()
|
75
|
+
while 0 < queue_count()
|
76
|
+
wait_finish()
|
77
|
+
stop()
|
78
|
+
break if 0 == queue_count()
|
79
|
+
start()
|
80
|
+
end
|
81
|
+
until @@actors.empty?
|
82
|
+
a = @@actors.pop()
|
83
|
+
a.close()
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end # Env
|
88
|
+
end # Opee
|
data/lib/opee/log.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Opee
|
5
|
+
class Log < Actor
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
super(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
# use ask() to invoke private methods
|
13
|
+
|
14
|
+
def set_options(options)
|
15
|
+
@logger = Logger.new(STDOUT)
|
16
|
+
severity = options[:severity] if options.has_key?(:severity)
|
17
|
+
|
18
|
+
# TBD options for setting a file, severity, max_file_size, max_file_count, formatter
|
19
|
+
end
|
20
|
+
|
21
|
+
def severity=(level)
|
22
|
+
if level.is_a?(String)
|
23
|
+
severity = {
|
24
|
+
'FATAL' => Logger::Severity::FATAL,
|
25
|
+
'ERROR' => Logger::Severity::ERROR,
|
26
|
+
'WARN' => Logger::Severity::WARN,
|
27
|
+
'INFO' => Logger::Severity::INFO,
|
28
|
+
'DEBUG' => Logger::Severity::DEBUG
|
29
|
+
}[level]
|
30
|
+
raise "#{level} is not a severity" if severity.nil?
|
31
|
+
level = severity
|
32
|
+
elsif level < Logger::Severity::DEBUG || Logger::Severity::FATAL < level
|
33
|
+
raise "#{level} is not a severity"
|
34
|
+
end
|
35
|
+
@logger.level = level
|
36
|
+
end
|
37
|
+
|
38
|
+
def log(severity, message)
|
39
|
+
@logger.add(severity, message)
|
40
|
+
end
|
41
|
+
|
42
|
+
end # Log
|
43
|
+
end # Opee
|
data/lib/opee/version.rb
CHANGED
data/test/relay.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require 'opee'
|
3
|
+
|
4
|
+
class Relay < ::Opee::Actor
|
5
|
+
attr_reader :last_data
|
6
|
+
|
7
|
+
def initialize(buddy)
|
8
|
+
super
|
9
|
+
@buddy = buddy
|
10
|
+
@last_data = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def relay(data)
|
16
|
+
@last_data = data
|
17
|
+
@buddy.ask(:relay, data) unless @buddy.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
end # Relay
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW2
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
[ File.dirname(__FILE__),
|
5
|
+
File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
].each { |path| $: << path unless $:.include?(path) }
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'opee'
|
10
|
+
require 'relay'
|
11
|
+
|
12
|
+
class OpeeTest < ::Test::Unit::TestCase
|
13
|
+
|
14
|
+
def test_opee_actor_queue
|
15
|
+
a = ::Relay.new(nil)
|
16
|
+
assert_equal(0, a.queue_count())
|
17
|
+
a.stop()
|
18
|
+
a.ask(:relay, 5)
|
19
|
+
assert_equal(1, a.queue_count())
|
20
|
+
a.ask(:relay, 7)
|
21
|
+
assert_equal(2, a.queue_count())
|
22
|
+
a.close()
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_opee_actor_ask
|
26
|
+
a = ::Relay.new(nil)
|
27
|
+
a.ask(:relay, 7)
|
28
|
+
sleep(0.5) # minimize dependencies for simplest possible test
|
29
|
+
assert_equal(7, a.last_data)
|
30
|
+
a.close()
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_opee_actor_method_missing
|
34
|
+
a = ::Relay.new(nil)
|
35
|
+
a.relay(7)
|
36
|
+
::Opee::Env.wait_close()
|
37
|
+
assert_equal(7, a.last_data)
|
38
|
+
a.close()
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_opee_actor_raise_after_close
|
42
|
+
a = ::Relay.new(nil)
|
43
|
+
a.close()
|
44
|
+
assert_raise(ThreadError) { a.start() }
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_opee_actor_priority
|
48
|
+
a = ::Relay.new(nil)
|
49
|
+
a.priority_ask(:relay, 7)
|
50
|
+
::Opee::Env.wait_close()
|
51
|
+
assert_equal(7, a.last_data)
|
52
|
+
a.close()
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_opee_actor_idle
|
56
|
+
a = ::Relay.new(nil)
|
57
|
+
a.on_idle(:relay, 7)
|
58
|
+
::Opee::Env.wait_close()
|
59
|
+
assert_equal(7, a.last_data)
|
60
|
+
a.close()
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_opee_actor_order
|
64
|
+
a = ::Relay.new(nil)
|
65
|
+
a.stop()
|
66
|
+
a.on_idle(:relay, 17)
|
67
|
+
a.priority_ask(:relay, 3)
|
68
|
+
a.ask(:relay, 7)
|
69
|
+
a.step()
|
70
|
+
assert_equal(3, a.last_data)
|
71
|
+
a.step()
|
72
|
+
assert_equal(7, a.last_data)
|
73
|
+
a.step()
|
74
|
+
assert_equal(17, a.last_data)
|
75
|
+
a.close()
|
76
|
+
end
|
77
|
+
|
78
|
+
end # OpeeTest
|
data/test/tc_opee_env.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW2
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
[ File.dirname(__FILE__),
|
5
|
+
File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
].each { |path| $: << path unless $:.include?(path) }
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'opee'
|
10
|
+
require 'relay'
|
11
|
+
|
12
|
+
class OpeeTest < ::Test::Unit::TestCase
|
13
|
+
|
14
|
+
def test_wait_close
|
15
|
+
a = ::Relay.new(nil)
|
16
|
+
a.ask(:relay, 7)
|
17
|
+
::Opee::Env.wait_close()
|
18
|
+
assert_equal(7, a.last_data)
|
19
|
+
a.close()
|
20
|
+
end
|
21
|
+
|
22
|
+
end # OpeeTest
|
data/test/tc_opee_log.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW2
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
[ File.dirname(__FILE__),
|
5
|
+
File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
].each { |path| $: << path unless $:.include?(path) }
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'opee'
|
10
|
+
|
11
|
+
class OpeeTest < ::Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_opee_log_log
|
14
|
+
#::Opee::Env.logger.ask(:severity=, Logger::INFO)
|
15
|
+
::Opee::Env.logger.severity= Logger::INFO
|
16
|
+
::Opee::Env.log(Logger::INFO, "hello")
|
17
|
+
::Opee::Env.each_actor { |a| puts a.to_s }
|
18
|
+
sleep(0.2)
|
19
|
+
end
|
20
|
+
|
21
|
+
end # OpeeTest
|
data/test/tests.rb
CHANGED
@@ -5,24 +5,7 @@ $: << File.join(File.dirname(__FILE__), "../lib")
|
|
5
5
|
|
6
6
|
require 'test/unit'
|
7
7
|
require 'opee'
|
8
|
-
|
9
|
-
class Relay < ::Opee::Actor
|
10
|
-
attr_reader :last_data
|
11
|
-
|
12
|
-
def initialize(buddy)
|
13
|
-
super
|
14
|
-
@buddy = buddy
|
15
|
-
@last_data = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def relay(data)
|
21
|
-
@last_data = data
|
22
|
-
@buddy.ask(:relay, data) unless @buddy.nil?
|
23
|
-
end
|
24
|
-
|
25
|
-
end # Relay
|
8
|
+
require 'relay'
|
26
9
|
|
27
10
|
class Opeet < ::Test::Unit::TestCase
|
28
11
|
|
@@ -33,8 +16,25 @@ class Opeet < ::Test::Unit::TestCase
|
|
33
16
|
a.ask(:relay, 7)
|
34
17
|
assert_equal(1, a.queue_count())
|
35
18
|
a.start()
|
19
|
+
sleep(0.5)
|
20
|
+
assert_equal(7, a.last_data)
|
21
|
+
a.close()
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_log
|
25
|
+
#::Opee::Env.logger.ask(:severity=, Logger::INFO)
|
26
|
+
::Opee::Env.logger.severity= Logger::INFO
|
27
|
+
::Opee::Env.log(Logger::INFO, "hello")
|
28
|
+
::Opee::Env.each_actor { |a| puts a.to_s }
|
36
29
|
sleep(0.2)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_wait_close
|
33
|
+
a = ::Relay.new(nil)
|
34
|
+
a.ask(:relay, 7)
|
35
|
+
::Opee::Env.wait_close()
|
37
36
|
assert_equal(7, a.last_data)
|
37
|
+
a.close()
|
38
38
|
end
|
39
39
|
|
40
40
|
end # Opeet
|
data/test/ts_opee.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby -wW2
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
[ File.dirname(__FILE__),
|
5
|
+
File.join(File.dirname(__FILE__), "../lib")
|
6
|
+
].each { |path| $: << path unless $:.include?(path) }
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'opee'
|
10
|
+
|
11
|
+
class OpeeTest < ::Test::Unit::TestCase
|
12
|
+
end # OpeeTest
|
13
|
+
|
14
|
+
require 'tc_opee_actor'
|
15
|
+
require 'tc_opee_log'
|
16
|
+
require 'tc_opee_env'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'An experimental Object-base Parallel Evaluation Environment. '
|
15
15
|
email: peter@ohler.com
|
@@ -19,9 +19,16 @@ extra_rdoc_files:
|
|
19
19
|
- README.md
|
20
20
|
files:
|
21
21
|
- lib/opee/actor.rb
|
22
|
+
- lib/opee/env.rb
|
23
|
+
- lib/opee/log.rb
|
22
24
|
- lib/opee/version.rb
|
23
25
|
- lib/opee.rb
|
26
|
+
- test/relay.rb
|
27
|
+
- test/tc_opee_actor.rb
|
28
|
+
- test/tc_opee_env.rb
|
29
|
+
- test/tc_opee_log.rb
|
24
30
|
- test/tests.rb
|
31
|
+
- test/ts_opee.rb
|
25
32
|
- LICENSE
|
26
33
|
- README.md
|
27
34
|
homepage: http://www.ohler.com/opee
|
@@ -46,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
53
|
version: '0'
|
47
54
|
requirements: []
|
48
55
|
rubyforge_project: opee
|
49
|
-
rubygems_version: 1.8.
|
56
|
+
rubygems_version: 1.8.11
|
50
57
|
signing_key:
|
51
58
|
specification_version: 3
|
52
59
|
summary: An experimental Object-base Parallel Evaluation Environment.
|