mmcs_urm 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f8183b0ad83cedd32ee5aed3f06186b47a1131340e896ff153b9cc7b1ce91909
4
+ data.tar.gz: 71151b0f5847bab6c78611071f78db1c4a4330578465c18cbce0ea2f3bbc05e8
5
+ SHA512:
6
+ metadata.gz: 6a061b01d25f881f5b8a9c9abe7e98a4744eb3779b53738bec4825838d609d7723c08ae3d22d302475ec51da8cc5a50706ff4df8dbee9122e17a5d6d2ab7d99c
7
+ data.tar.gz: 7fc7950dc763f16b7b2749201617c4a9968eba5564d83515ca4654fd81c055be918344f4d3fb0fc6e303ff6bc2f52273562fc5cc6972ac1c4a2c96edd9b46d02
data/.rubocop.yml ADDED
@@ -0,0 +1,336 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ EnforcedStyle: double_quotes
10
+
11
+ Metrics/MethodLength:
12
+ Max: 40
13
+
14
+ Metrics/ParameterLists:
15
+ Max: 6
16
+
17
+ Metrics/ClassLength:
18
+ Max: 150
19
+
20
+ Lint/ScriptPermission:
21
+ Enabled: false
22
+
23
+ Layout/EndOfLine:
24
+ Enabled: false
25
+
26
+ Layout/LineLength:
27
+ Max: 120
28
+
29
+ Gemspec/DeprecatedAttributeAssignment:
30
+ Enabled: true
31
+
32
+ Gemspec/DevelopmentDependencies:
33
+ Enabled: true
34
+
35
+ Gemspec/RequireMFA:
36
+ Enabled: true
37
+
38
+ Layout/LineContinuationLeadingSpace:
39
+ Enabled: true
40
+
41
+ Layout/LineContinuationSpacing:
42
+ Enabled: true
43
+
44
+ Layout/LineEndStringConcatenationIndentation:
45
+ Enabled: true
46
+
47
+ Layout/SpaceBeforeBrackets:
48
+ Enabled: true
49
+
50
+ Lint/AmbiguousAssignment:
51
+ Enabled: true
52
+
53
+ Lint/AmbiguousOperatorPrecedence:
54
+ Enabled: true
55
+
56
+ Lint/AmbiguousRange:
57
+ Enabled: true
58
+
59
+ Lint/ConstantOverwrittenInRescue:
60
+ Enabled: true
61
+
62
+ Lint/DeprecatedConstants:
63
+ Enabled: true
64
+
65
+ Lint/DuplicateBranch:
66
+ Enabled: true
67
+
68
+ Lint/DuplicateMagicComment:
69
+ Enabled: true
70
+
71
+ Lint/DuplicateMatchPattern:
72
+ Enabled: true
73
+
74
+ Lint/DuplicateRegexpCharacterClassElement:
75
+ Enabled: true
76
+
77
+ Lint/EmptyBlock:
78
+ Enabled: true
79
+
80
+ Lint/EmptyClass:
81
+ Enabled: true
82
+
83
+ Lint/EmptyInPattern:
84
+ Enabled: true
85
+
86
+ Lint/IncompatibleIoSelectWithFiberScheduler:
87
+ Enabled: true
88
+
89
+ Lint/ItWithoutArgumentsInBlock:
90
+ Enabled: true
91
+
92
+ Lint/LambdaWithoutLiteralBlock:
93
+ Enabled: true
94
+
95
+ Lint/LiteralAssignmentInCondition:
96
+ Enabled: true
97
+
98
+ Lint/MixedCaseRange:
99
+ Enabled: true
100
+
101
+ Lint/NoReturnInBeginEndBlocks:
102
+ Enabled: true
103
+
104
+ Lint/NonAtomicFileOperation:
105
+ Enabled: true
106
+
107
+ Lint/NumberedParameterAssignment:
108
+ Enabled: true
109
+
110
+ Lint/OrAssignmentToConstant:
111
+ Enabled: true
112
+
113
+ Lint/RedundantDirGlobSort:
114
+ Enabled: true
115
+
116
+ Lint/RedundantRegexpQuantifiers:
117
+ Enabled: true
118
+
119
+ Lint/RefinementImportMethods:
120
+ Enabled: true
121
+
122
+ Lint/RequireRangeParentheses:
123
+ Enabled: true
124
+
125
+ Lint/RequireRelativeSelfPath:
126
+ Enabled: true
127
+
128
+ Lint/SymbolConversion:
129
+ Enabled: true
130
+
131
+ Lint/ToEnumArguments:
132
+ Enabled: true
133
+
134
+ Lint/TripleQuotes:
135
+ Enabled: true
136
+
137
+ Lint/UnexpectedBlockArity:
138
+ Enabled: true
139
+
140
+ Lint/UnmodifiedReduceAccumulator:
141
+ Enabled: true
142
+
143
+ Lint/UselessRescue:
144
+ Enabled: true
145
+
146
+ Lint/UselessRuby2Keywords:
147
+ Enabled: true
148
+
149
+ Metrics/CollectionLiteralLength:
150
+ Enabled: true
151
+
152
+ Naming/BlockForwarding:
153
+ Enabled: true
154
+
155
+ Security/CompoundHash:
156
+ Enabled: true
157
+
158
+ Security/IoMethods:
159
+ Enabled: true
160
+
161
+ Style/ArgumentsForwarding:
162
+ Enabled: true
163
+
164
+ Style/ArrayIntersect:
165
+ Enabled: true
166
+
167
+ Style/CollectionCompact:
168
+ Enabled: true
169
+
170
+ Style/ComparableClamp:
171
+ Enabled: true
172
+
173
+ Style/ConcatArrayLiterals:
174
+ Enabled: true
175
+
176
+ Style/DataInheritance:
177
+ Enabled: true
178
+
179
+ Style/DirEmpty:
180
+ Enabled: true
181
+
182
+ Style/DocumentDynamicEvalDefinition:
183
+ Enabled: true
184
+
185
+ Style/EmptyHeredoc:
186
+ Enabled: true
187
+
188
+ Style/EndlessMethod:
189
+ Enabled: true
190
+
191
+ Style/EnvHome:
192
+ Enabled: true
193
+
194
+ Style/ExactRegexpMatch:
195
+ Enabled: true
196
+
197
+ Style/FetchEnvVar:
198
+ Enabled: true
199
+
200
+ Style/FileEmpty:
201
+ Enabled: true
202
+
203
+ Style/FileRead:
204
+ Enabled: true
205
+
206
+ Style/FileWrite:
207
+ Enabled: true
208
+
209
+ Style/HashConversion:
210
+ Enabled: true
211
+
212
+ Style/HashExcept:
213
+ Enabled: true
214
+
215
+ Style/IfWithBooleanLiteralBranches:
216
+ Enabled: true
217
+
218
+ Style/InPatternThen:
219
+ Enabled: true
220
+
221
+ Style/MagicCommentFormat:
222
+ Enabled: true
223
+
224
+ Style/MapCompactWithConditionalBlock:
225
+ Enabled: true
226
+
227
+ Style/MapIntoArray:
228
+ Enabled: true
229
+
230
+ Style/MapToHash:
231
+ Enabled: true
232
+
233
+ Style/MapToSet:
234
+ Enabled: true
235
+
236
+ Style/MinMaxComparison:
237
+ Enabled: true
238
+
239
+ Style/MultilineInPatternThen:
240
+ Enabled: true
241
+
242
+ Style/NegatedIfElseCondition:
243
+ Enabled: true
244
+
245
+ Style/NestedFileDirname:
246
+ Enabled: true
247
+
248
+ Style/NilLambda:
249
+ Enabled: true
250
+
251
+ Style/NumberedParameters:
252
+ Enabled: true
253
+
254
+ Style/NumberedParametersLimit:
255
+ Enabled: true
256
+
257
+ Style/ObjectThen:
258
+ Enabled: true
259
+
260
+ Style/OpenStructUse:
261
+ Enabled: true
262
+
263
+ Style/OperatorMethodCall:
264
+ Enabled: true
265
+
266
+ Style/QuotedSymbols:
267
+ Enabled: true
268
+
269
+ Style/RedundantArgument:
270
+ Enabled: true
271
+
272
+ Style/RedundantArrayConstructor:
273
+ Enabled: true
274
+
275
+ Style/RedundantConstantBase:
276
+ Enabled: true
277
+
278
+ Style/RedundantCurrentDirectoryInPath:
279
+ Enabled: true
280
+
281
+ Style/RedundantDoubleSplatHashBraces:
282
+ Enabled: true
283
+
284
+ Style/RedundantEach:
285
+ Enabled: true
286
+
287
+ Style/RedundantFilterChain:
288
+ Enabled: true
289
+
290
+ Style/RedundantHeredocDelimiterQuotes:
291
+ Enabled: true
292
+
293
+ Style/RedundantInitialize:
294
+ Enabled: true
295
+
296
+ Style/RedundantLineContinuation:
297
+ Enabled: true
298
+
299
+ Style/RedundantRegexpArgument:
300
+ Enabled: true
301
+
302
+ Style/RedundantRegexpConstructor:
303
+ Enabled: true
304
+
305
+ Style/RedundantSelfAssignmentBranch:
306
+ Enabled: true
307
+
308
+ Style/RedundantStringEscape:
309
+ Enabled: true
310
+
311
+ Style/ReturnNilInPredicateMethodDefinition:
312
+ Enabled: true
313
+
314
+ Style/SelectByRegexp:
315
+ Enabled: true
316
+
317
+ Style/SendWithLiteralMethodName:
318
+ Enabled: true
319
+
320
+ Style/SingleLineDoEndBlock:
321
+ Enabled: true
322
+
323
+ Style/StringChars:
324
+ Enabled: true
325
+
326
+ Style/SuperArguments:
327
+ Enabled: true
328
+
329
+ Style/SuperWithArgsParentheses:
330
+ Enabled: true
331
+
332
+ Style/SwapValues:
333
+ Enabled: true
334
+
335
+ Style/YAMLFileRead:
336
+ Enabled: true
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-06-01
4
+
5
+ - Initial release
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ [INSERT CONTACT METHOD].
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 YBarsukova, Teachyyou, LSN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # About URM Gem
2
+
3
+ This gem is designed for convenient and fast construction and verification of URM - Unbounded Register Machines. It will be especially useful for students studying algorithm theory.
4
+
5
+ ## Installation
6
+
7
+ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
16
+
17
+ ## Usage
18
+
19
+ TODO: Write usage instructions here
20
+
21
+ ## Development
22
+
23
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
24
+
25
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/demo_gem. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/demo_gem/blob/master/CODE_OF_CONDUCT.md).
30
+
31
+ ## License
32
+
33
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
34
+
35
+ ## Code of Conduct
36
+
37
+ Everyone interacting in the DemoGem project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/demo_gem/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
data/lib/urm/coder.rb ADDED
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "urm/exceptions"
4
+ require "urm/instruction"
5
+ require "prime"
6
+
7
+ module Urm
8
+ # The Coder class provides methods for encoding and decoding URM instructions
9
+ # and entire machines using Godel numbering. This class is useful for converting instructions and machines
10
+ # into unique numerical representations and back. The primary methods available in this class are:
11
+ #
12
+ # - `self.code_single_instruction`: Encodes a single URM instruction into a Godel number.
13
+ # - `self.code_machine`: Encodes an entire URM machine (a sequence of instructions) into an array of Godel numbers.
14
+ # - `self.godel_code`: Encodes an array of integers into a single Godel number.
15
+ #
16
+ # TODO add about decoding
17
+ class Coder
18
+ def self.code_single_instruction(instruction)
19
+ code = case instruction.type
20
+ when :set
21
+ [1, instruction.label, instruction.register, instruction.value]
22
+ when :inc
23
+ [2, instruction.label, instruction.register]
24
+ when :dec
25
+ [3, instruction.label, instruction.register]
26
+ when :if
27
+ [4, instruction.label, instruction.register, instruction.label_true, instruction.label_false]
28
+ when :stop
29
+ [5, instruction.label]
30
+ else
31
+ raise "Unknown instruction type: #{instruction.type}"
32
+ end
33
+ godel_code(code)
34
+ end
35
+
36
+ def self.code_machine(machine)
37
+ machine.validate_instructions
38
+ machine.instructions.compact.map do |instruction|
39
+ code_single_instruction(instruction)
40
+ end
41
+ end
42
+
43
+ def self.godel_code(numbers)
44
+ primes = Prime.first(numbers.size)
45
+ godel_number = 1
46
+
47
+ numbers.each_with_index do |num, index|
48
+ godel_number *= primes[index]**(num + 1)
49
+ end
50
+
51
+ godel_number
52
+ end
53
+
54
+ def self.decode_single_instruction(godel_number)
55
+ exponents = extract_exponents(godel_number)
56
+
57
+ type = exponents[0]
58
+ label = exponents[1]
59
+
60
+ case type
61
+ when 1
62
+ decode_set(label, exponents)
63
+ when 2
64
+ decode_inc(label, exponents)
65
+ when 3
66
+ decode_dec(label, exponents)
67
+ when 4
68
+ decode_if(label, exponents)
69
+ when 5
70
+ decode_stop(label)
71
+ else
72
+ raise "Unknown instruction type: #{type}"
73
+ end
74
+ end
75
+
76
+ def self.decode_machine(godel_numbers)
77
+ instructions = godel_numbers.map { |number| decode_single_instruction(number) }
78
+ machine = Machine.new(0) # Создаем машину с 0 входными регистрами
79
+ instructions.each { |instruction| machine.add(instruction) }
80
+ machine
81
+ end
82
+
83
+ def self.extract_exponents(godel_number)
84
+ exponents = []
85
+ prime_enum = Prime.each
86
+
87
+ loop do
88
+ prime = prime_enum.next
89
+ exponent = 0
90
+
91
+ while (godel_number % prime).zero?
92
+ godel_number /= prime
93
+ exponent += 1
94
+ end
95
+
96
+ exponents << (exponent - 1)
97
+ break if godel_number == 1
98
+ end
99
+
100
+ exponents
101
+ end
102
+
103
+ def self.decode_set(label, exponents)
104
+ Instruction.set(label, exponents[2], exponents[3])
105
+ end
106
+
107
+ def self.decode_inc(label, exponents)
108
+ Instruction.inc(label, exponents[2])
109
+ end
110
+
111
+ def self.decode_dec(label, exponents)
112
+ Instruction.dec(label, exponents[2])
113
+ end
114
+
115
+ def self.decode_if(label, exponents)
116
+ Instruction.if(label, exponents[2], exponents[3], exponents[4])
117
+ end
118
+
119
+ def self.decode_stop(label)
120
+ Instruction.stop(label)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urm
4
+ class InvalidRegisterInitialization < StandardError; end
5
+ class InvalidRegisterIndex < StandardError; end
6
+ class InvalidLabel < StandardError; end
7
+ class MultipleStopsError < StandardError; end
8
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "urm/exceptions"
4
+
5
+ module Urm
6
+ # This class represents an instruction for the Unbounded Register Machine (URM).
7
+ # It includes various types of instructions such as setting a register,
8
+ # incrementing a register, decrementing a register, conditional jumps, stop, and copy.
9
+ class Instruction
10
+ attr_reader :label, :type, :register, :value, :label_true, :label_false
11
+
12
+ def initialize(label, type, register: nil, value: nil, label_true: nil, label_false: nil)
13
+ raise Urm::InvalidLabel, "Label must be positive" if label <= 0
14
+
15
+ @label = label
16
+ @type = type
17
+ @register = register
18
+ @value = value
19
+ @label_true = label_true
20
+ @label_false = label_false
21
+ end
22
+
23
+ def self.set(label, register, value)
24
+ raise Urm::InvalidRegisterInitialization, "Value cannot be negative" if value.negative?
25
+ raise Urm::InvalidRegisterIndex, "Register index must be positive" if register <= 0
26
+
27
+ new(label, :set, register: register, value: value)
28
+ end
29
+
30
+ def self.inc(label, register)
31
+ raise Urm::InvalidRegisterIndex, "Register index must be positive" if register <= 0
32
+
33
+ new(label, :inc, register: register)
34
+ end
35
+
36
+ def self.dec(label, register)
37
+ raise Urm::InvalidRegisterIndex, "Register index must be positive" if register <= 0
38
+
39
+ new(label, :dec, register: register)
40
+ end
41
+
42
+ def self.if(label, register, label_true, label_false)
43
+ raise Urm::InvalidRegisterIndex, "Register index must be positive" if register <= 0
44
+ raise Urm::InvalidLabel, "Label must be positive" if label_true <= 0 || label_false <= 0
45
+
46
+ new(label, :if, register: register, label_true: label_true, label_false: label_false)
47
+ end
48
+
49
+ def self.stop(label)
50
+ new(label, :stop)
51
+ end
52
+
53
+ # For copy instructions index of source register is stored in value field
54
+ def self.copy(label, register, source_register)
55
+ raise Urm::InvalidRegisterIndex, "Register index must be positive" if register <= 0 || source_register <= 0
56
+
57
+ new(label, :copy, register: register, value: source_register)
58
+ end
59
+
60
+ def to_s
61
+ case @type
62
+ when :set
63
+ "#{@label}. x#{@register} = #{@value}"
64
+ when :inc
65
+ "#{@label}. x#{@register} = x#{@register} + 1"
66
+ when :dec
67
+ "#{@label}. x#{@register} = x#{@register} - 1"
68
+ when :if
69
+ "#{@label}. if x#{@register} == 0 goto #{@label_true} else goto #{@label_false}"
70
+ when :stop
71
+ "#{@label}. stop"
72
+ when :copy
73
+ "#{@label}. x#{@register} = x#{@value}"
74
+ else
75
+ raise "Unknown instruction type: #{@type}"
76
+ end
77
+ end
78
+
79
+ def self.parse(instruction_str)
80
+ if (match = instruction_str.match(/^(-?\d+)\. x(\d+) = (-?\d+)$/))
81
+ parse_set(match)
82
+ elsif (match = instruction_str.match(/^(-?\d+)\. x(\d+) = x\2 \+ 1$/))
83
+ parse_inc(match)
84
+ elsif (match = instruction_str.match(/^(-?\d+)\. x(\d+) = x\2 - 1$/))
85
+ parse_dec(match)
86
+ elsif (match = instruction_str.match(/^(-?\d+)\. if x(\d+) == 0 goto (\d+) else goto (\d+)$/))
87
+ parse_if(match)
88
+ elsif (match = instruction_str.match(/^(-?\d+)\. x(\d+) = x(\d+)$/))
89
+ parse_copy(match)
90
+ elsif (match = instruction_str.match(/^(-?\d+)\. stop$/))
91
+ parse_stop(match)
92
+ else
93
+ raise "Unknown instruction format: #{instruction_str}"
94
+ end
95
+ end
96
+
97
+ class << self
98
+ private
99
+
100
+ def parse_set(match)
101
+ set(match[1].to_i, match[2].to_i, match[3].to_i)
102
+ end
103
+
104
+ def parse_inc(match)
105
+ inc(match[1].to_i, match[2].to_i)
106
+ end
107
+
108
+ def parse_dec(match)
109
+ dec(match[1].to_i, match[2].to_i)
110
+ end
111
+
112
+ def parse_if(match)
113
+ self.if(match[1].to_i, match[2].to_i, match[3].to_i, match[4].to_i)
114
+ end
115
+
116
+ def parse_copy(match)
117
+ copy(match[1].to_i, match[2].to_i, match[3].to_i)
118
+ end
119
+
120
+ def parse_stop(match)
121
+ stop(match[1].to_i)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "urm/instruction"
4
+ require "urm/exceptions"
5
+
6
+ module Urm
7
+ # The Machine class represents an Unlimited Register Machine (URM)
8
+ # capable of executing a series of instructions on a set of registers.
9
+ # Machine is initialized with a specified number of input parameters.
10
+ class Machine
11
+ attr_reader :registers, :instructions
12
+
13
+ # Initializes a new URM machine with n input parameters
14
+ #
15
+ # @param input_size [Integer] The number of input parameters
16
+ def initialize(input_size)
17
+ @registers = Array.new(input_size + 1, 0)
18
+ @instructions = []
19
+ end
20
+
21
+ # Adds an instruction to the machine
22
+ #
23
+ # @param instruction [Urm::Instruction, String] The instruction to add
24
+ def add(instruction)
25
+ instruction =
26
+ case instruction
27
+ when String
28
+ Urm::Instruction.parse(instruction)
29
+ else
30
+ instruction
31
+ end
32
+ @instructions[instruction.label - 1] = instruction
33
+ end
34
+
35
+ # Adds an array of instructions to the machine
36
+ #
37
+ # @param instructions [Array<Urm::Instruction, String>] The array of instructions to add
38
+ def add_all(instructions)
39
+ instructions.each do |instruction|
40
+ add(instruction)
41
+ end
42
+ end
43
+
44
+ # Runs the machine with the given input values and returns the value of x1
45
+ #
46
+ # @param inputs [Array<Integer>] The input values
47
+ # @return [Integer] The value of x1 after execution
48
+ def run(*inputs)
49
+ validate_instructions
50
+ initialize_registers(inputs)
51
+ execute_instructions
52
+ end
53
+
54
+ # Runs the machine with the given input values and prints the value of x1
55
+ #
56
+ # @param inputs [Array<Integer>] The input values
57
+ def run_and_print(*inputs)
58
+ result = run(*inputs)
59
+ puts result
60
+ end
61
+
62
+ # Validates the instructions to ensure there are no references to non-existent labels
63
+ # and there is exactly one stop instruction.
64
+ def validate_instructions
65
+ labels = collect_labels
66
+ validate_labels(labels)
67
+ validate_stop_instructions
68
+ end
69
+
70
+ private
71
+
72
+ # Initialize the registers with input values
73
+ #
74
+ # @param inputs [Array<Integer>] The input values
75
+ def initialize_registers(inputs)
76
+ inputs.each_with_index do |value, index|
77
+ @registers[index + 2] = value
78
+ end
79
+ @registers[1] = 0 # Output register starts with 0
80
+ end
81
+
82
+ # Execute the instructions loaded into the machine
83
+ #
84
+ # @return [Integer] The value of x1 after execution
85
+ def execute_instructions
86
+ pc = 0 # Program counter starts at 0
87
+ while pc < @instructions.length
88
+ instruction = @instructions[pc]
89
+ if instruction.nil?
90
+ pc += 1
91
+ next
92
+ end
93
+ new_pc = execute_instruction(instruction, pc)
94
+ pc = new_pc.nil? ? pc + 1 : new_pc
95
+ end
96
+ @registers[1]
97
+ end
98
+
99
+ # Execute a single instruction
100
+ #
101
+ # @param instruction [Urm::Instruction] The instruction to execute
102
+ # @param counter [Integer] The current program counter
103
+ # @return [Integer, nil] The updated program counter or nil if the pc should just increment
104
+ def execute_instruction(instruction, counter)
105
+ case instruction.type
106
+ when :set
107
+ execute_set(instruction)
108
+ when :inc
109
+ execute_inc(instruction)
110
+ when :dec
111
+ execute_dec(instruction)
112
+ when :if
113
+ return execute_if(instruction)
114
+ when :copy
115
+ execute_copy(instruction)
116
+ when :stop
117
+ return counter + 1 # Stop execution by setting pc beyond the last instruction
118
+ else
119
+ raise "Unknown instruction type: #{instruction.type}"
120
+ end
121
+ nil
122
+ end
123
+
124
+ def execute_set(instruction)
125
+ @registers[instruction.register] = instruction.value
126
+ end
127
+
128
+ def execute_inc(instruction)
129
+ @registers[instruction.register] += 1
130
+ end
131
+
132
+ def execute_dec(instruction)
133
+ @registers[instruction.register] -= 1 if @registers[instruction.register].positive?
134
+ end
135
+
136
+ def execute_if(instruction)
137
+ if @registers[instruction.register].zero?
138
+ instruction.label_true - 1
139
+ else
140
+ instruction.label_false - 1
141
+ end
142
+ end
143
+
144
+ def execute_copy(instruction)
145
+ @registers[instruction.register] = @registers[instruction.value]
146
+ end
147
+
148
+ # Collects all labels referenced by if instructions
149
+ #
150
+ # @return [Array<Integer>] An array of labels
151
+ def collect_labels
152
+ @instructions.compact.select { |instr| instr.type == :if }
153
+ .flat_map { |instr| [instr.label_true, instr.label_false] }
154
+ end
155
+
156
+ # Validates that all referenced labels exist
157
+ #
158
+ # @param labels [Array<Integer>] An array of labels to validate
159
+ # @return [void]
160
+ def validate_labels(labels)
161
+ max_valid_label = @instructions.compact.size
162
+ labels.each do |label|
163
+ next if label.positive? && (label <= max_valid_label || (label == max_valid_label + 1 && !stop_exists?))
164
+
165
+ raise InvalidLabel, "Instruction references a non-existent label: #{label}"
166
+ end
167
+ end
168
+
169
+ # Validates that there is exactly one stop instruction
170
+ #
171
+ # @return [void]
172
+ def validate_stop_instructions
173
+ stop_count = @instructions.compact.count { |instr| instr.type == :stop }
174
+
175
+ raise MultipleStopsError, "There are multiple stop instructions" if stop_count > 1
176
+
177
+ return unless stop_count.zero?
178
+
179
+ @instructions[@instructions.compact.size] = Urm::Instruction.stop(@instructions.compact.size + 1)
180
+ end
181
+
182
+ def stop_exists?
183
+ @instructions.compact.any? { |instr| instr.type == :stop }
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "urm/machine"
4
+ require "urm/exceptions"
5
+
6
+ # The MachineTester class provides methods for testing the functionality of an
7
+ # Unbounded Register Machine (URM). It includes methods to assert that the machine
8
+ # produces expected outputs for given inputs and to validate the machine's outputs
9
+ # over a range of input values using a provided lambda function.
10
+ #
11
+ # Methods:
12
+ #
13
+ # - self.assert(machine, expected_output, *inputs):
14
+ # Checks that for the given input values, the machine produces the expected output.
15
+ # If the output does not match the expected value, it prints an error message.
16
+ # Returns true if the output matches, false otherwise.
17
+ #
18
+ # - self.assertRange(machine, a, b, func):
19
+ # Validates that for all combinations of input values within the range [a, b],
20
+ # the machine's output matches the output of the provided lambda function.
21
+ # It handles any number of input parameters for the machine.
22
+ # Returns true if all outputs match, false otherwise.
23
+ #
24
+ # Example usage:
25
+ #
26
+ # machine = Urm::Machine.new(2)
27
+ # multiplication_lambda = ->(x, y) { x * y }
28
+ # MachineTester.assert(machine, 6, 2, 3) # Asserts that machine.run(2, 3) == 6
29
+ # MachineTester.assertRange(machine, 0, 10, multiplication_lambda) # Validates for range [0, 10]
30
+ #
31
+ class MachineTester
32
+ # Проверяет, что на входе х2... х(n+1) машина дает х1
33
+ #
34
+ # @param machine [Urm::Machine] The machine to test
35
+ # @param inputs [Array<Integer>] The input values
36
+ # @param expected_output [Integer] The expected output
37
+ # @return [Boolean] true if the machine's output matches the expected output
38
+ def self.assert(machine, expected_output, *inputs)
39
+ output = machine.run(*inputs)
40
+ if output == expected_output
41
+ true
42
+ else
43
+ puts "Test failed for input #{inputs}: expected #{expected_output}, got #{output}"
44
+ false
45
+ end
46
+ end
47
+
48
+ # Проверяет, что на всех наборах входных в диапазоне [a,b] machine.run(эти наборы) = lambda(эти наборы)
49
+ #
50
+ # @param machine [Urm::Machine] The machine to test
51
+ # @param lower_bound [Integer] The start of the range
52
+ # @param upper_bound [Integer] The end of the range
53
+ # @param func [Proc] The lambda function to test against
54
+ # @return [Boolean] true if all outputs match the lambda's outputs
55
+ def self.assert_range(machine, lower_bound, upper_bound, func)
56
+ input_size = machine.registers.size - 1 # учитываем x1, который не является входным
57
+
58
+ ranges = Array.new(input_size) { (lower_bound..upper_bound).to_a }
59
+ inputs_list = ranges.first.product(*ranges[1..])
60
+
61
+ inputs_list.each do |inputs|
62
+ expected_output = func.call(*inputs)
63
+ return false unless assert(machine, expected_output, *inputs)
64
+ end
65
+
66
+ true
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Urm
4
+ VERSION = "0.1.0"
5
+ end
data/lib/urm.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "urm/version"
4
+ require "urm/instruction"
5
+ require "urm/machine"
6
+ require "urm/machine_tester"
7
+
8
+ # The Urm module provides functionality to create and manipulate instructions
9
+ # for the Unbounded Register Machine (URM). The URM is a theoretical model used
10
+ # in computer science to study the principles of computation and algorithms.
11
+ #
12
+ # This module includes classes for different types of instructions such as setting
13
+ # a register, incrementing a register, decrementing a register, conditional jumps,
14
+ # and stopping the machine. It also provides methods to parse instructions from
15
+ # strings and convert them back to their string representations.
16
+ module Urm
17
+ class Error < StandardError; end
18
+
19
+ # Машина для умножения
20
+ multiplication_machine = Urm::Machine.new(2)
21
+
22
+ multiplication_instructions = [
23
+ "1. if x3 == 0 goto 9 else goto 2", # if x3 == 0 goto 9 else 2
24
+ "2. x3 = x3 - 1", # x3 = x3 - 1
25
+ "3. x4 = x2", # x4 = x2
26
+ "4. if x4 == 0 goto 8 else goto 5", # if x4 == 0 goto 8 else 5
27
+ "5. x1 = x1 + 1", # x1 = x1 + 1
28
+ "6. x4 = x4 - 1", # x4 = x4 - 1
29
+ "7. if x4 == 0 goto 8 else goto 5", # if x4 == 0 goto 8 else 5
30
+ "8. if x3 == 0 goto 9 else goto 2", # if x3 == 0 goto 9 else 2
31
+ "9. stop" # stop
32
+ ]
33
+
34
+ # Добавляем массив строк с парсингом
35
+ multiplication_machine.add_all(multiplication_instructions)
36
+
37
+ MachineTester.assert_range(multiplication_machine, 0, 100, ->(x, y) { x * y })
38
+
39
+ # Машина для деления
40
+ division_machine = Urm::Machine.new(2)
41
+
42
+ division_instructions = [
43
+ Urm::Instruction.if(1, 2, 10, 2), # 1. if x2 == 0 goto 10 else 2
44
+ Urm::Instruction.copy(2, 4, 3), # 2. x4 = x3
45
+ Urm::Instruction.dec(3, 2), # 3. x2 = x2 - 1
46
+ Urm::Instruction.dec(4, 4), # 4. x4 = x4 - 1
47
+ Urm::Instruction.if(5, 2, 6, 7), # 5. if x2 == 0 goto 6 else 7
48
+ Urm::Instruction.if(6, 4, 8, 10), # 6. if x4 == 0 goto 8 else 10
49
+ Urm::Instruction.if(7, 4, 8, 3), # 7. if x4 == 0 goto 8 else 3
50
+ Urm::Instruction.inc(8, 1), # 8. x1 = x1 + 1
51
+ Urm::Instruction.if(9, 2, 10, 2), # 9. if x2 == 0 goto 10 else 2
52
+ Urm::Instruction.stop(10) # 10. stop
53
+ ]
54
+
55
+ # Добавляем массив инструкций
56
+ division_machine.add_all(division_instructions)
57
+
58
+ MachineTester.assert_range(division_machine, 1, 100, ->(x, y) { x / y })
59
+ end
data/sig/urm/coder.rbs ADDED
@@ -0,0 +1,19 @@
1
+ module Urm
2
+ class Coder
3
+
4
+ def self.code_machine: -> Array[Integer]
5
+ def self.code_single_instruction: -> Integer
6
+
7
+ def self.decode_dec: -> Instruction
8
+ def self.decode_if: -> Instruction
9
+ def self.decode_inc: -> Instruction
10
+ def self.decode_machine: -> Machine
11
+ def self.decode_set: -> Instruction
12
+ def self.decode_single_instruction: -> Instruction
13
+ def self.decode_stop: -> Instruction
14
+
15
+ def self.extract_exponents: -> Array[Integer]
16
+
17
+ def self.godel_code: -> Integer
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Urm
2
+ class Instruction
3
+
4
+ def self.set: (Integer, Integer) -> Instruction
5
+ def self.inc: (Integer) -> Instruction
6
+ def self.dec: (Integer) -> Instruction
7
+ def self.if: (Integer, Integer, Integer) -> Instruction
8
+ def self.stop: () -> Instruction
9
+ def self.copy: (Integer, Integer) -> Instruction
10
+
11
+ def self.parse: -> Instruction
12
+
13
+ def self.parse_set: -> Instruction
14
+ def self.parse_inc: -> Instruction
15
+ def self.parse_dec: -> Instruction
16
+ def self.parse_if: -> Instruction
17
+ def self.parse_stop: -> Instruction
18
+ def self.parse_copy: -> Instruction
19
+
20
+ attr_reader label: Integer
21
+ attr_reader label_false: Integer?
22
+ attr_reader label_true: Integer?
23
+ attr_reader register: Integer?
24
+ attr_reader type: Symbol
25
+ attr_reader value: Integer?
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Urm
2
+ class Machine
3
+
4
+ attr_reader instructions: Array[Instruction]
5
+ attr_reader registers: Array[Integer]
6
+
7
+ def add: -> void
8
+ def add_all: -> void
9
+
10
+ def run: -> Integer
11
+ def run_and_print: -> void
12
+
13
+ private
14
+
15
+ def execute_instruction: -> Integer?
16
+ def execute_instructions: -> Integer
17
+ def initialize_registers: -> void
18
+ def execute_set: -> void
19
+ def execute_inc: -> void
20
+ def execute_dec: -> void
21
+ def execute_if: -> void
22
+ def execute_copy: -> void
23
+
24
+ def stop_exists?: -> bool
25
+
26
+ def validate_instructions: -> void
27
+ def collect_labels: -> Array[Integer]
28
+ def validate_labels: -> void
29
+ def validate_stop_instructions: -> void
30
+
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ class MachineTester
2
+ def self.assert: -> bool
3
+ def self.assert_range: -> bool
4
+ end
data/sig/urm.rbs ADDED
@@ -0,0 +1,5 @@
1
+ module Urm
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mmcs_urm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - LSN
8
+ - YBarsukova
9
+ - Teachyyou
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2024-06-03 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Made in learning purposes by MMCS students for MMCS students
16
+ email:
17
+ - ''
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ".rubocop.yml"
23
+ - CHANGELOG.md
24
+ - CODE_OF_CONDUCT.md
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - lib/urm.rb
29
+ - lib/urm/coder.rb
30
+ - lib/urm/exceptions.rb
31
+ - lib/urm/instruction.rb
32
+ - lib/urm/machine.rb
33
+ - lib/urm/machine_tester.rb
34
+ - lib/urm/version.rb
35
+ - sig/urm.rbs
36
+ - sig/urm/coder.rbs
37
+ - sig/urm/instruction.rbs
38
+ - sig/urm/machine.rbs
39
+ - sig/urm/machine_tester.rbs
40
+ homepage: https://github.com/YBarsukova/urm_dev
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://github.com/YBarsukova/urm_dev
45
+ source_code_uri: https://github.com/YBarsukova/urm_dev
46
+ changelog_uri: https://github.com/YBarsukova/urm_dev
47
+ rubygems_mfa_required: 'true'
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.0.0
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.5.9
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: A gem for creating and testing basic normalized URM machines
67
+ test_files: []