jungi 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a47af5b592b2644e0e5bd53a0553e8a13fc447a
4
- data.tar.gz: f8b3727413f664466e92e061e1bf43b58aeb6f09
3
+ metadata.gz: 87b366600908ca9405f46e66540d769f9bc4e959
4
+ data.tar.gz: 89db92476c6237d23a7d328706c5c8ba56b9b650
5
5
  SHA512:
6
- metadata.gz: cf8ae4baae6b93c295976a5804a12ac4b8a62b28c7d678b00c58ef4863d6922efb31e934ff412eddb1de8eabd59179993e941565ae994b00116a2b7f1809385e
7
- data.tar.gz: b7916b8904e86d262e6e5d21f874ee9eb263f1ec3a3fc5bdbdb7cf7f42851dfa7938579d4984f778eb814b7869266f4c6ee26bdc485d4426b0def09e55b87ee3
6
+ metadata.gz: 152d1ff9312b8556e36b3acdc0addb638dbaefa589ee989704279c6358ab8c3144fc75d6535f8cb096a722d05ab4647c18b2e4d81b68c52e6e89bf19502cd270
7
+ data.tar.gz: de7c5a8ddb3f194583933c1ed6b0febea259b93adaab123ded48403914907222c8455401f0905300ba3571eb1656cdff1ee0de4a9a11a7bcc32e8e3054492862
data/Rakefile CHANGED
@@ -2,37 +2,37 @@
2
2
 
3
3
  # Thanks, fhsjaagshs!
4
4
 
5
- s = eval(File.read(Dir.glob("*.gemspec").first))
5
+ s = eval(File.read(Dir.glob('*.gemspec').first))
6
6
  app_name = s.name
7
7
  gem_name = "#{s.name}-#{s.version}.gem"
8
8
 
9
- desc "Cleanup gem files"
9
+ desc 'Cleanup gem files'
10
10
  task :clean do
11
- `[ -f #{gem_name} ] && rm #{gem_name}`
11
+ `[ -f #{gem_name} ] && rm #{gem_name}`
12
12
  end
13
13
 
14
- desc "Build gem"
14
+ desc 'Build gem'
15
15
  task :build do
16
- puts "Building '#{app_name}'..."
17
- `gem build -q #{app_name}.gemspec`
16
+ puts "Building '#{app_name}'..."
17
+ `gem build -q #{app_name}.gemspec`
18
18
  end
19
19
 
20
- desc "Install gem"
20
+ desc 'Install gem'
21
21
  task :install do
22
- puts "Uninstalling currently installed '#{app_name}'..."
23
- `gem uninstall -q --force #{gem_name}`
24
- puts "Installing '#{app_name}'..."
25
- `gem install -q --ignore-dependencies --local #{gem_name}`
22
+ puts "Uninstalling currently installed '#{app_name}'..."
23
+ `gem uninstall -q --force #{gem_name}`
24
+ puts "Installing '#{app_name}'..."
25
+ `gem install -q --ignore-dependencies --local #{gem_name}`
26
26
  end
27
27
 
28
- desc "Upload gem to rubyforge"
28
+ desc 'Upload gem to rubyforge'
29
29
  task :push do
30
- Rake::Task["clean"].invoke
31
- Rake::Task["build"].invoke
32
- puts "Pushing '#{app_name}' to rubygems..."
33
- exec("gem push #{gem_name}")
30
+ Rake::Task['clean'].invoke
31
+ Rake::Task['build'].invoke
32
+ puts "Pushing '#{app_name}' to rubygems..."
33
+ exec("gem push #{gem_name}")
34
34
  end
35
35
 
36
36
  task :default do
37
- Rake::Task["build"].invoke
37
+ Rake::Task['build'].invoke
38
38
  end
data/bin/jungi CHANGED
@@ -32,8 +32,6 @@ rescue LoadError
32
32
  require 'jungi/cli'
33
33
  end
34
34
 
35
- WIDTH = JungiCli.width
36
-
37
35
  options = {}
38
36
  ARGV.options do |opts|
39
37
  opts.banner = 'Usage: jungi-cli [options]'
@@ -46,6 +44,9 @@ ARGV.options do |opts|
46
44
  opts.on('-B', '--bigger', 'Use IPIP Big Five Broad 100 test') do |_v|
47
45
  options[:bigfivebroad100] = true
48
46
  end
47
+ opts.on('-c', '--control', "Use Delroy L. Paulhus' SOC3") do |_v|
48
+ options[:soc3test] = true
49
+ end
49
50
  opts.on('-j', '--jungian', 'Use OEJTS 1.2 test') do |_v|
50
51
  options[:oejts] = true
51
52
  end
@@ -67,7 +68,7 @@ ARGV.options do |opts|
67
68
  opts.parse!
68
69
  end
69
70
 
70
- if WIDTH < 80
71
+ if JungiCli.width < 80
71
72
  STDERR.puts options[:helper] + "\n"
72
73
  STDERR.puts "Minimum shell width is 80 characters (#{WIDTH})"
73
74
  exit
@@ -77,6 +78,7 @@ tests += 1 if options[:bigfivebroad50]
77
78
  tests += 1 if options[:bigfivebroad100]
78
79
  tests += 1 if options[:oejts]
79
80
  tests += 1 if options[:sd3test]
81
+ tests += 1 if options[:soc3test]
80
82
 
81
83
  if tests > 1
82
84
  STDERR.puts options[:helper] + "\n"
@@ -84,6 +86,7 @@ if tests > 1
84
86
  exit
85
87
  end
86
88
 
89
+ # -----------------------------------------------------------------------------
87
90
  if options[:oejts]
88
91
  current_test = OEJTSTest.new
89
92
  JungiCli.clr
@@ -91,15 +94,15 @@ if options[:oejts]
91
94
  JungiCli.line
92
95
 
93
96
  32.times do |int|
94
- puts "Question #{int + 1}".center WIDTH
97
+ puts "Question #{int + 1}".center JungiCli.width
95
98
  s = JungiCli.ask_scale((current_test.question int), options[:randomize])
96
99
  current_test.set_answer int, s
97
100
  end
98
101
  desig, iev, snv, ftv, jpv = current_test.result
99
102
 
100
- puts('Test Complete'.center WIDTH)
101
- puts('Designation'.center WIDTH, '-')
102
- puts(desig.center WIDTH)
103
+ puts('Test Complete'.center JungiCli.width)
104
+ puts('Designation'.center JungiCli.width, '-')
105
+ puts(desig.center JungiCli.width)
103
106
  JungiCli.line
104
107
  JungiCli.display_doc(OEJTSTest.parse_result(desig, iev, snv, ftv, jpv))
105
108
  JungiCli.line
@@ -110,17 +113,18 @@ elsif options[:bigfivebroad50]
110
113
  JungiCli.line
111
114
 
112
115
  50.times do |c|
113
- puts "Question #{c + 1}".center WIDTH
116
+ puts "Question #{c + 1}".center JungiCli.width
114
117
  s = JungiCli.ask_scale((current_test.question c), options[:randomize])
115
118
  current_test.set_answer c, s
116
119
  end
117
120
  ex, agr, cons, emo, int = current_test.result
118
- puts('Test Complete'.center WIDTH)
121
+ puts('Test Complete'.center JungiCli.width)
119
122
  JungiCli.line
120
123
  JungiCli.display_doc("Extraversion: #{ex} Agreeableness: #{agr}\n" \
121
124
  "Conscientiousness: #{cons} Emotional Stability: #{emo}\n" \
122
125
  "Intellect: #{int}")
123
126
  JungiCli.line
127
+ # -----------------------------------------------------------------------------
124
128
  elsif options[:bigfivebroad100]
125
129
  current_test = BigFiveBroad100Test.new
126
130
  JungiCli.clr
@@ -128,17 +132,18 @@ elsif options[:bigfivebroad100]
128
132
  JungiCli.line
129
133
 
130
134
  100.times do |num|
131
- puts "Question #{num + 1}".center WIDTH
135
+ puts "Question #{num + 1}".center JungiCli.width
132
136
  s = JungiCli.ask_scale((current_test.question num), options[:randomize])
133
137
  current_test.set_answer num, s
134
138
  end
135
139
  ex, agr, cons, emo, int = current_test.result
136
- puts('Test Complete'.center WIDTH)
140
+ puts('Test Complete'.center JungiCli.width)
137
141
  JungiCli.line
138
142
  JungiCli.display_doc("Extraversion: #{ex} Agreeableness: #{agr}\n" \
139
143
  "Conscientiousness: #{cons} Emotional Stability: #{emo}\n" \
140
144
  "Intellect: #{int}")
141
145
  JungiCli.line
146
+ # -----------------------------------------------------------------------------
142
147
  elsif options[:sd3test]
143
148
  current_test = SD3Test.new
144
149
  JungiCli.clr
@@ -146,15 +151,33 @@ elsif options[:sd3test]
146
151
  JungiCli.line
147
152
 
148
153
  27.times do |c|
149
- puts "Question #{c + 1}".center WIDTH
154
+ puts "Question #{c + 1}".center JungiCli.width
150
155
  s = JungiCli.ask_scale((current_test.question c), options[:randomize])
151
156
  current_test.set_answer c, s
152
157
  end
153
158
  mach, narc, psycho = current_test.result
154
- puts('Test Complete'.center WIDTH)
159
+ puts('Test Complete'.center JungiCli.width)
155
160
  JungiCli.line
156
161
  JungiCli.display_doc(SD3Test.parse_result(mach, narc, psycho))
157
162
  JungiCli.line
163
+ # -----------------------------------------------------------------------------
164
+ elsif options[:soc3test]
165
+ current_test = SOC3Test.new
166
+ JungiCli.clr
167
+ JungiCli.display_doc(SOC3Test::DOC)
168
+ JungiCli.line
169
+
170
+ 30.times do |c|
171
+ puts "Question #{c + 1}".center JungiCli.width
172
+ s = JungiCli.ask_scale7((current_test.question c), options[:randomize])
173
+ current_test.set_answer c, s
174
+ end
175
+ pers, inter, socio = current_test.result
176
+ puts('Test Complete'.center JungiCli.width)
177
+ JungiCli.line
178
+ JungiCli.display_doc(SOC3Test.parse_result(pers, inter, socio))
179
+ JungiCli.line
180
+ # -----------------------------------------------------------------------------
158
181
  else
159
182
  STDERR.puts options[:helper] + "\n"
160
183
  STDERR.puts 'You must specify a test type'
@@ -20,6 +20,7 @@ module Question
20
20
  module Type
21
21
  YESORNO = :yn
22
22
  SCALE = :scale
23
+ SCALE7 = :scale7
23
24
  end
24
25
 
25
26
  # Question Answer Module
@@ -38,7 +39,7 @@ module Question
38
39
 
39
40
  # Check value for scale value
40
41
  def self.scale?(value)
41
- if value == 1 || value == 2 || value == 3 || value == 4 || value == 5
42
+ if (value.instance_of? Fixnum) && value.between?(1, 5)
42
43
  return true
43
44
  else
44
45
  return false
@@ -53,12 +54,31 @@ module Question
53
54
  [5, 4, 3, 2, 1].to_a[value - 1]
54
55
  end
55
56
 
57
+ # Check value for scale7 value
58
+ def self.scale7?(value)
59
+ if (value.instance_of? Fixnum) && value.between?(1, 7)
60
+ return true
61
+ else
62
+ return false
63
+ end
64
+ end
65
+
66
+ # Reverse scale7 value
67
+ def self.reverse_scale7(value)
68
+ unless Question::Answer.scale7?(value)
69
+ fail "#{type} is not a proper scale7!"
70
+ end
71
+ [7, 6, 5, 4, 3, 2, 1].to_a[value - 1]
72
+ end
73
+
56
74
  # Checks value follows type
57
75
  def self.follows_type?(type, value)
58
76
  if type == Question::Type::YESORNO
59
77
  return Question::Answer.yes_or_no?(value)
60
78
  elsif type == Question::Type::SCALE
61
79
  return Question::Answer.scale?(value)
80
+ elsif type == Question::Type::SCALE7
81
+ return Question::Answer.scale7?(value)
62
82
  else
63
83
  fail "#{type} is an invalid question type!"
64
84
  end
@@ -191,3 +211,21 @@ class ScaleTest < Test
191
211
  end
192
212
  end
193
213
  end
214
+
215
+ # Final Scale7 Test Class
216
+ class Scale7Test < ScaleTest
217
+ # Set question answer to value
218
+ def set_answer(index, value)
219
+ self.out_of_index? index
220
+ fail "#{value} is not a scale!" unless Question::Answer.scale7?(value)
221
+ @answers[index] = value
222
+ end
223
+
224
+ # Randomize the answers to a scale test
225
+ def randomize!
226
+ self.class.const_get(:QUESTIONS).length.times do |num|
227
+ set_answer(num, rand(1..7))
228
+ end
229
+ nil
230
+ end
231
+ end
@@ -31,6 +31,16 @@ module JungiCli
31
31
  80
32
32
  end
33
33
 
34
+ # Ask for integer
35
+ def self.ask_integer(prompt)
36
+ result = nil
37
+ until yield result
38
+ print prompt
39
+ result = gets.chomp.to_i
40
+ end
41
+ result
42
+ end
43
+
34
44
  # Fetch prototype objects
35
45
  def self.scale_proto
36
46
  start = (' |1|2|3|4|5| '.center width)
@@ -44,31 +54,35 @@ module JungiCli
44
54
  s1, c1, s2 = scale_proto
45
55
  s1[s1.length - p1.length, s1.length - 1] = p1
46
56
  s2[0, p2.length - 1] = p2
47
- s1 + c1 + s2
57
+ res = s1 + c1 + s2
58
+ if res.length > width + 1
59
+ res = (p1 + ' |1|2|3|4|5| ' + p2).center width
60
+ if res.length > width
61
+ return "#{p1}\n |1|2|3|4|5| \n#{p2}"
62
+ else
63
+ return res
64
+ end
65
+ else
66
+ return res
67
+ end
48
68
  else
49
- (p1 + ' |1|2|3|4|5| ').center WIDTH
50
- end
51
- end
52
-
53
- # Ask for integer
54
- def self.ask_integer(prompt)
55
- result = nil
56
- until yield result
57
- print prompt
58
- result = gets.chomp.to_i
69
+ "#{p1}\n |1|2|3|4|5| "
59
70
  end
60
- result
61
71
  end
62
72
 
63
73
  # Nicely ask a scale
64
74
  def self.ask_scale(scale, randomize = false)
65
- puts parse_scale scale
75
+ blah = parse_scale scale
76
+ if /\n/.match blah
77
+ display_doc blah
78
+ else
79
+ puts blah
80
+ end
66
81
 
67
82
  if randomize
68
83
  int = rand(1..5)
69
84
  puts "> #{int}"
70
85
  int
71
-
72
86
  else
73
87
  ask_integer '> ' do |r|
74
88
  Question::Answer.scale? r
@@ -76,6 +90,55 @@ module JungiCli
76
90
  end
77
91
  end
78
92
 
93
+ # Fetch prototype objects7
94
+ def self.scale7_proto
95
+ start = (' |1|2|3|4|5|6|7| '.center width)
96
+ start.partition(' |1|2|3|4|5|6|7| ')
97
+ end
98
+
99
+ # Handle question via scale7
100
+ def self.parse_scale7(scale)
101
+ p1, p2 = scale.split '|'
102
+ if p2
103
+ s1, c1, s2 = scale7_proto
104
+ s1[s1.length - p1.length, s1.length - 1] = p1
105
+ s2[0, p2.length - 1] = p2
106
+ res = s1 + c1 + s2
107
+ if res.length > width + 1
108
+ res = (p1 + ' |1|2|3|4|5|6|7| ' + p2).center width
109
+ if res.length > width
110
+ return "#{p1}\n |1|2|3|4|5|6|7| \n#{p2}"
111
+ else
112
+ return res
113
+ end
114
+ else
115
+ return res
116
+ end
117
+ else
118
+ "#{p1}\n |1|2|3|4|5|6|7| "
119
+ end
120
+ end
121
+
122
+ # Nicely ask a scale7
123
+ def self.ask_scale7(scale, randomize = false)
124
+ blah = parse_scale7 scale
125
+ if /\n/.match blah
126
+ display_doc blah
127
+ else
128
+ puts blah
129
+ end
130
+
131
+ if randomize
132
+ int = rand(1..7)
133
+ puts "> #{int}"
134
+ int
135
+ else
136
+ ask_integer '> ' do |r|
137
+ Question::Answer.scale7? r
138
+ end
139
+ end
140
+ end
141
+
79
142
  # Pad a document to width
80
143
  def self.center_doc(doc)
81
144
  doc = doc.split "\n"
@@ -146,7 +146,7 @@ END_FILE
146
146
  if ftv.between?(0, 1)
147
147
  "Lukewarm #{word}, "
148
148
  elsif ftv < 4
149
- "Weak(#{ftv}) #{word} "
149
+ "Weak(#{ftv}) #{word}, "
150
150
  else
151
151
  "Strong(#{ftv}) #{word}, "
152
152
  end
@@ -118,13 +118,118 @@ END_FILE
118
118
 
119
119
  def result
120
120
  mach = (self.Q24 + self.Q19 + self.Q1 + self.Q2 + self.Q10 +
121
- self.Q21 + self.Q12 + self.Q22 + self.Q23) / 9.0
121
+ self.Q21 + self.Q12 + self.Q22 + self.Q23) / 9.0
122
122
  narc = (self.Q9 + Question::Answer.reverse_scale(self.Q18) +
123
- self.Q8 + self.Q14 + self.Q6 + Question::Answer.reverse_scale(self.Q7) +
124
- self.Q15 + Question::Answer.reverse_scale(self.Q11) + self.Q25) / 9.0
123
+ self.Q8 + self.Q14 + self.Q6 +
124
+ Question::Answer.reverse_scale(self.Q7) +
125
+ self.Q15 + Question::Answer.reverse_scale(self.Q11) + self.Q25) / 9.0
125
126
  psycho = (self.Q20 + Question::Answer.reverse_scale(self.Q5) +
126
- self.Q27 + self.Q4 + self.Q13 + self.Q26 +
127
- Question::Answer.reverse_scale(self.Q3) + self.Q16 + self.Q17) / 9.0
127
+ self.Q27 + self.Q4 + self.Q13 + self.Q26 +
128
+ Question::Answer.reverse_scale(self.Q3) + self.Q16 + self.Q17) / 9.0
128
129
  [mach, narc, psycho]
129
130
  end
130
131
  end
132
+
133
+ # Spheres of Control Version 3
134
+ class SOC3Test < Scale7Test
135
+ DOC = <<END_FILE
136
+ Spheres of Control Version 3
137
+ -
138
+ Delroy L. Paulhus
139
+
140
+ This test evaluates how you perceive certain
141
+ spheres of control. Answer each question with
142
+ a "1" for Completely Disagree, "2" for Strongly
143
+ Disagree, "3" for Disagree, "4" for Neither
144
+ Agree nor Disagree, "5" for Agree, "6" for
145
+ Strongly Disagree, and "7" for Completely Agree.
146
+ Press <ENTER> after you've made your choice.
147
+ END_FILE
148
+ QUESTIONS = [
149
+ 'I can usually achieve what I want if I work hard for it.',
150
+ "In my personal relationships, the other person usually has more\n"\
151
+ 'control than I do.',
152
+ "By taking an active part in political and social affairs, we the\n"\
153
+ ' people can influence world events.',
154
+ 'Once I make plans, I am almost certain to make them work.',
155
+ 'I have no trouble making and keeping friends.',
156
+ 'The average citizen can have an influence on government decisions.',
157
+ 'I prefer games involving some luck over games requiring pure skill.',
158
+ 'I\'m not good at guiding the course of a conversation with several'\
159
+ ' others.',
160
+ "It is difficult for us to have much control over the things\n"\
161
+ 'politicians do in office.',
162
+ 'I can learn almost anything if I set my mind to it.',
163
+ "I can usually develop a personal relationship with someone I find\n"\
164
+ 'appealing.',
165
+ "Bad economic conditions are caused by world events that are beyond\n"\
166
+ 'our control.',
167
+ 'My major accomplishments are entirely due to my hard work and ability.',
168
+ 'I can usually steer a conversation toward the topics I want to talk'\
169
+ ' about.',
170
+ 'With enough effort we can wipe out political corruption.',
171
+ "I usually do not set goals because I have a hard time following\n"\
172
+ 'through on them.',
173
+ "When I need assistance with something, I often find it difficult to\n"\
174
+ 'get others to help.',
175
+ "One of the major reasons we have wars is because people don\'t take\n"\
176
+ 'enough interest in politics.',
177
+ 'Bad luck has sometimes prevented me from achieving things.',
178
+ 'If there\'s someone I want to meet, I can usually arrange it.',
179
+ "There is nothing we, as consumers, can do to keep the cost of living\n"\
180
+ 'from going higher.',
181
+ 'Almost anything is possible for me if I really want it.',
182
+ 'I often find it hard to get my point of view across to others.',
183
+ 'It is impossible to have any real influence over what big businesses do.',
184
+ 'Most of what happens in my career is beyond my control.',
185
+ 'In attempting to smooth over a disagreement, I sometimes make it worse.',
186
+ "I prefer to concentrate my energy on other things rather than on\n"\
187
+ 'solving the world\'s problems.',
188
+ "I find it pointless to keep working on something that\'s too diffic"\
189
+ 'ult for me.',
190
+ 'I find it easy to play an important part in most group situations.',
191
+ "In the long run, we the voters are responsible for bad government\n"\
192
+ 'on a national as well as a local level.'
193
+ ]
194
+
195
+ def self.parse_pers(pers)
196
+ "Personal Control #{pers}, "
197
+ end
198
+
199
+ def self.parse_inter(inter)
200
+ "Interpersonal Control #{inter}, "
201
+ end
202
+
203
+ def self.parse_socio(socio)
204
+ "Socio-Political Control #{socio}"
205
+ end
206
+
207
+ def self.parse_result(pers, inter, socio)
208
+ pers = (pers.round 1)
209
+ inter = (inter.round 1)
210
+ socio = (socio.round 1)
211
+ parse_pers(pers) << parse_inter(inter) << parse_socio(socio)
212
+ end
213
+
214
+ def result
215
+ pers = (self.Q1 + self.Q4 + self.Q10 + self.Q13 +
216
+ self.Q22 + Question::Answer.reverse_scale7(self.Q7) +
217
+ Question::Answer.reverse_scale7(self.Q16) +
218
+ Question::Answer.reverse_scale7(self.Q19) +
219
+ Question::Answer.reverse_scale7(self.Q25) +
220
+ Question::Answer.reverse_scale7(self.Q28)) / 10.0
221
+ inter = (self.Q5 + self.Q11 + self.Q14 + self.Q20 +
222
+ self.Q29 + Question::Answer.reverse_scale7(self.Q2) +
223
+ Question::Answer.reverse_scale7(self.Q8) +
224
+ Question::Answer.reverse_scale7(self.Q17) +
225
+ Question::Answer.reverse_scale7(self.Q23) +
226
+ Question::Answer.reverse_scale7(self.Q26)) / 10.0
227
+ socio = (self.Q3 + self.Q6 + self.Q15 + self.Q18 +
228
+ self.Q30 + Question::Answer.reverse_scale7(self.Q9) +
229
+ Question::Answer.reverse_scale7(self.Q12) +
230
+ Question::Answer.reverse_scale7(self.Q21) +
231
+ Question::Answer.reverse_scale7(self.Q24) +
232
+ Question::Answer.reverse_scale7(self.Q27)) / 10.0
233
+ [pers, inter, socio]
234
+ end
235
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jungi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - MFrogy Starmade
@@ -36,12 +36,12 @@ require_paths:
36
36
  - lib
37
37
  required_ruby_version: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: 2.0.0
42
42
  required_rubygems_version: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - '>='
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  requirements: []
@@ -49,5 +49,5 @@ rubyforge_project:
49
49
  rubygems_version: 2.4.8
50
50
  signing_key:
51
51
  specification_version: 4
52
- summary: Take tests.
52
+ summary: Added Spheres of Control Version 3.
53
53
  test_files: []