twisty_puzzles 0.0.1 → 0.0.6

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +6 -1
  4. data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
  5. data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
  6. data/ext/twisty_puzzles/native/cube_average.c +184 -0
  7. data/ext/twisty_puzzles/native/cube_average.h +5 -0
  8. data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
  9. data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
  10. data/ext/twisty_puzzles/native/cube_state.c +264 -0
  11. data/ext/twisty_puzzles/native/cube_state.h +31 -0
  12. data/ext/twisty_puzzles/native/extconf.rb +1 -1
  13. data/ext/twisty_puzzles/native/face_symbols.c +67 -0
  14. data/ext/twisty_puzzles/native/face_symbols.h +34 -0
  15. data/ext/twisty_puzzles/native/native.c +28 -0
  16. data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
  17. data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
  18. data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
  19. data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
  20. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
  21. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
  22. data/ext/twisty_puzzles/native/skewb_state.c +214 -0
  23. data/ext/twisty_puzzles/native/skewb_state.h +23 -0
  24. data/ext/twisty_puzzles/native/utils.c +76 -0
  25. data/ext/twisty_puzzles/native/utils.h +31 -0
  26. data/lib/twisty_puzzles.rb +38 -0
  27. data/lib/twisty_puzzles/abstract_direction.rb +38 -39
  28. data/lib/twisty_puzzles/abstract_move.rb +1 -2
  29. data/lib/twisty_puzzles/abstract_move_parser.rb +32 -33
  30. data/lib/twisty_puzzles/algorithm.rb +112 -113
  31. data/lib/twisty_puzzles/algorithm_transformation.rb +19 -21
  32. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +56 -56
  33. data/lib/twisty_puzzles/cancellation_helper.rb +124 -125
  34. data/lib/twisty_puzzles/color_scheme.rb +1 -1
  35. data/lib/twisty_puzzles/commutator.rb +82 -80
  36. data/lib/twisty_puzzles/compiled_algorithm.rb +31 -32
  37. data/lib/twisty_puzzles/compiled_cube_algorithm.rb +49 -50
  38. data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +18 -19
  39. data/lib/twisty_puzzles/coordinate.rb +243 -246
  40. data/lib/twisty_puzzles/cube.rb +494 -495
  41. data/lib/twisty_puzzles/cube_constants.rb +40 -41
  42. data/lib/twisty_puzzles/cube_direction.rb +15 -18
  43. data/lib/twisty_puzzles/cube_move.rb +285 -290
  44. data/lib/twisty_puzzles/cube_move_parser.rb +75 -76
  45. data/lib/twisty_puzzles/cube_print_helper.rb +133 -133
  46. data/lib/twisty_puzzles/cube_state.rb +80 -81
  47. data/lib/twisty_puzzles/move_type_creator.rb +17 -18
  48. data/lib/twisty_puzzles/parser.rb +176 -179
  49. data/lib/twisty_puzzles/part_cycle_factory.rb +39 -42
  50. data/lib/twisty_puzzles/puzzle.rb +16 -17
  51. data/lib/twisty_puzzles/reversible_applyable.rb +24 -25
  52. data/lib/twisty_puzzles/rotation.rb +76 -75
  53. data/lib/twisty_puzzles/skewb_direction.rb +14 -15
  54. data/lib/twisty_puzzles/skewb_move.rb +49 -49
  55. data/lib/twisty_puzzles/skewb_move_parser.rb +51 -51
  56. data/lib/twisty_puzzles/skewb_notation.rb +121 -118
  57. data/lib/twisty_puzzles/skewb_state.rb +120 -121
  58. data/lib/twisty_puzzles/state_helper.rb +20 -21
  59. data/lib/twisty_puzzles/sticker_cycle.rb +43 -44
  60. data/lib/twisty_puzzles/utils.rb +3 -0
  61. data/lib/twisty_puzzles/version.rb +3 -1
  62. metadata +30 -10
@@ -6,160 +6,159 @@ require 'twisty_puzzles/cube_constants'
6
6
  require 'twisty_puzzles/cube_state'
7
7
 
8
8
  module TwistyPuzzles
9
-
10
- # Helper class to figure out information about the cancellation between two algs.
11
- module CancellationHelper
12
- include CubeConstants
13
-
14
- def self.swap_to_end(algorithm, index)
15
- new_moves = algorithm.moves.dup
16
- index.upto(algorithm.length - 2) do |current_index|
17
- obstacle_index = current_index + 1
18
- current = new_moves[current_index]
19
- obstacle = new_moves[obstacle_index]
20
- return nil unless current.can_swap?(obstacle)
21
-
22
- new_moves[current_index], new_moves[obstacle_index] = current.swap(obstacle)
23
- end
24
- Algorithm.new(new_moves)
9
+ # Helper class to figure out information about the cancellation between two algs.
10
+ module CancellationHelper
11
+ include CubeConstants
12
+
13
+ def self.swap_to_end(algorithm, index)
14
+ new_moves = algorithm.moves.dup
15
+ index.upto(algorithm.length - 2) do |current_index|
16
+ obstacle_index = current_index + 1
17
+ current = new_moves[current_index]
18
+ obstacle = new_moves[obstacle_index]
19
+ return nil unless current.can_swap?(obstacle)
20
+
21
+ new_moves[current_index], new_moves[obstacle_index] = current.swap(obstacle)
25
22
  end
23
+ Algorithm.new(new_moves)
24
+ end
26
25
 
27
- # Possible variations of the algorithm where the last move has been swapped as much as allowed
28
- # (e.g. D U can swap).
29
- def self.cancel_variants(algorithm)
30
- variants = []
31
- algorithm.moves.each_index.reverse_each do |i|
32
- variant = swap_to_end(algorithm, i)
33
- break unless variant
34
-
35
- variants.push(variant)
36
- end
37
- raise if variants.empty?
26
+ # Possible variations of the algorithm where the last move has been swapped as much as allowed
27
+ # (e.g. D U can swap).
28
+ def self.cancel_variants(algorithm)
29
+ variants = []
30
+ algorithm.moves.each_index.reverse_each do |i|
31
+ variant = swap_to_end(algorithm, i)
32
+ break unless variant
38
33
 
39
- variants
34
+ variants.push(variant)
40
35
  end
36
+ raise if variants.empty?
41
37
 
42
- # Cancel this algorithm as much as possilbe
43
- def self.cancel(algorithm, cube_size)
44
- raise TypeError unless algorithm.is_a?(Algorithm)
38
+ variants
39
+ end
45
40
 
46
- CubeState.check_cube_size(cube_size)
47
- alg = Algorithm::EMPTY
48
- algorithm.moves.each do |m|
49
- alg = push_with_cancellation(alg, m, cube_size)
50
- end
51
- alg
52
- end
41
+ # Cancel this algorithm as much as possilbe
42
+ def self.cancel(algorithm, cube_size)
43
+ raise TypeError unless algorithm.is_a?(Algorithm)
53
44
 
54
- def self.combine_transformations(left, right)
55
- left.dup.transform_values { |e| right[e] }.freeze
45
+ CubeState.check_cube_size(cube_size)
46
+ alg = Algorithm::EMPTY
47
+ algorithm.moves.each do |m|
48
+ alg = push_with_cancellation(alg, m, cube_size)
56
49
  end
50
+ alg
51
+ end
57
52
 
58
- def self.apply_transformation_to!(transformation, face_state)
59
- face_state.map! { |f| transformation[f] }
60
- end
53
+ def self.combine_transformations(left, right)
54
+ left.dup.transform_values { |e| right[e] }.freeze
55
+ end
61
56
 
62
- TRIVIAL_CENTER_TRANSFORMATION = { U: :U, F: :F, R: :R, L: :L, B: :B, D: :D }.freeze
57
+ def self.apply_transformation_to!(transformation, face_state)
58
+ face_state.map! { |f| transformation[f] }
59
+ end
63
60
 
64
- def self.create_directed_transformations(basic_transformation, invert)
65
- twice = combine_transformations(basic_transformation, basic_transformation)
66
- thrice = combine_transformations(twice, basic_transformation)
67
- non_zero_transformations = [basic_transformation, twice, thrice]
68
- adjusted_non_zero_transformations =
69
- invert ? non_zero_transformations.reverse : non_zero_transformations
70
- [TRIVIAL_CENTER_TRANSFORMATION] + adjusted_non_zero_transformations
71
- end
61
+ TRIVIAL_CENTER_TRANSFORMATION = { U: :U, F: :F, R: :R, L: :L, B: :B, D: :D }.freeze
72
62
 
73
- CENTER_TRANSFORMATIONS =
74
- begin
75
- x_transformation = { U: :B, F: :U, R: :R, L: :L, B: :D, D: :F }.freeze
76
- y_transformation = { U: :U, F: :L, R: :F, L: :B, B: :R, D: :D }.freeze
77
- z_transformation = { U: :R, F: :F, R: :D, L: :U, B: :B, D: :L }.freeze
78
- {
79
- U: create_directed_transformations(y_transformation, false),
80
- F: create_directed_transformations(z_transformation, false),
81
- R: create_directed_transformations(x_transformation, false),
82
- L: create_directed_transformations(x_transformation, true),
83
- B: create_directed_transformations(z_transformation, true),
84
- D: create_directed_transformations(y_transformation, true)
85
- }
86
- end
63
+ def self.create_directed_transformations(basic_transformation, invert)
64
+ twice = combine_transformations(basic_transformation, basic_transformation)
65
+ thrice = combine_transformations(twice, basic_transformation)
66
+ non_zero_transformations = [basic_transformation, twice, thrice]
67
+ adjusted_non_zero_transformations =
68
+ invert ? non_zero_transformations.reverse : non_zero_transformations
69
+ [TRIVIAL_CENTER_TRANSFORMATION] + adjusted_non_zero_transformations
70
+ end
87
71
 
88
- def self.center_transformation(rotation)
89
- CENTER_TRANSFORMATIONS[rotation.axis_face.face_symbol][rotation.direction.value]
72
+ CENTER_TRANSFORMATIONS =
73
+ begin
74
+ x_transformation = { U: :B, F: :U, R: :R, L: :L, B: :D, D: :F }.freeze
75
+ y_transformation = { U: :U, F: :L, R: :F, L: :B, B: :R, D: :D }.freeze
76
+ z_transformation = { U: :R, F: :F, R: :D, L: :U, B: :B, D: :L }.freeze
77
+ {
78
+ U: create_directed_transformations(y_transformation, false),
79
+ F: create_directed_transformations(z_transformation, false),
80
+ R: create_directed_transformations(x_transformation, false),
81
+ L: create_directed_transformations(x_transformation, true),
82
+ B: create_directed_transformations(z_transformation, true),
83
+ D: create_directed_transformations(y_transformation, true)
84
+ }
90
85
  end
91
86
 
92
- def self.rotated_center_state(rotations)
93
- rotations.reduce(FACE_SYMBOLS.dup) do |center_state, rotation|
94
- apply_transformation_to!(center_transformation(rotation), center_state)
95
- end
96
- end
87
+ def self.center_transformation(rotation)
88
+ CENTER_TRANSFORMATIONS[rotation.axis_face.face_symbol][rotation.direction.value]
89
+ end
97
90
 
98
- def self.combined_rotation_algs
99
- Rotation::NON_ZERO_ROTATIONS.collect_concat do |left|
100
- second_rotations =
101
- Rotation::NON_ZERO_ROTATIONS.reject do |e|
102
- e.direction.double_move? || e.same_axis?(left)
103
- end
104
- second_rotations.map { |right| Algorithm.new([left, right]) }
105
- end
91
+ def self.rotated_center_state(rotations)
92
+ rotations.reduce(FACE_SYMBOLS.dup) do |center_state, rotation|
93
+ apply_transformation_to!(center_transformation(rotation), center_state)
106
94
  end
95
+ end
107
96
 
108
- def self.rotation_sequences
109
- @rotation_sequences ||=
110
- begin
111
- trivial_rotation_algs = [Algorithm::EMPTY]
112
- single_rotation_algs = Rotation::NON_ZERO_ROTATIONS.map { |e| Algorithm.move(e) }
113
- combined_rotation_algs = self.combined_rotation_algs
114
- rotation_algs = trivial_rotation_algs + single_rotation_algs + combined_rotation_algs
115
- rotation_algs.map do |alg|
116
- [rotated_center_state(alg.moves), alg]
117
- end.to_h.freeze
97
+ def self.combined_rotation_algs
98
+ Rotation::NON_ZERO_ROTATIONS.collect_concat do |left|
99
+ second_rotations =
100
+ Rotation::NON_ZERO_ROTATIONS.reject do |e|
101
+ e.direction.double_move? || e.same_axis?(left)
118
102
  end
103
+ second_rotations.map { |right| Algorithm.new([left, right]) }
119
104
  end
105
+ end
120
106
 
121
- def self.cancelled_rotations(rotations)
122
- center_state = rotated_center_state(rotations)
123
- rotation_sequences[center_state]
124
- end
107
+ def self.rotation_sequences
108
+ @rotation_sequences ||=
109
+ begin
110
+ trivial_rotation_algs = [Algorithm::EMPTY]
111
+ single_rotation_algs = Rotation::NON_ZERO_ROTATIONS.map { |e| Algorithm.move(e) }
112
+ combined_rotation_algs = self.combined_rotation_algs
113
+ rotation_algs = trivial_rotation_algs + single_rotation_algs + combined_rotation_algs
114
+ rotation_algs.map do |alg|
115
+ [rotated_center_state(alg.moves), alg]
116
+ end.to_h.freeze
117
+ end
118
+ end
119
+
120
+ def self.cancelled_rotations(rotations)
121
+ center_state = rotated_center_state(rotations)
122
+ rotation_sequences[center_state]
123
+ end
125
124
 
126
- def self.num_tail_rotations(algorithm)
127
- num = 0
128
- algorithm.moves.reverse_each do |e|
129
- break unless e.is_a?(Rotation)
125
+ def self.num_tail_rotations(algorithm)
126
+ num = 0
127
+ algorithm.moves.reverse_each do |e|
128
+ break unless e.is_a?(Rotation)
130
129
 
131
- num += 1
132
- end
133
- num
130
+ num += 1
134
131
  end
132
+ num
133
+ end
135
134
 
136
- def self.alg_plus_cancelled_move(algorithm, move, cube_size)
137
- if move.is_a?(Rotation) && (tail_rotations = num_tail_rotations(algorithm)) >= 2
138
- Algorithm.new(algorithm.moves[0...-tail_rotations]) +
139
- cancelled_rotations(algorithm.moves[-tail_rotations..-1] + [move])
140
- else
141
- Algorithm.new(algorithm.moves[0...-1]) +
142
- algorithm.moves[-1].join_with_cancellation(move, cube_size)
143
- end
135
+ def self.alg_plus_cancelled_move(algorithm, move, cube_size)
136
+ if move.is_a?(Rotation) && (tail_rotations = num_tail_rotations(algorithm)) >= 2
137
+ Algorithm.new(algorithm.moves[0...-tail_rotations]) +
138
+ cancelled_rotations(algorithm.moves[-tail_rotations..] + [move])
139
+ else
140
+ Algorithm.new(algorithm.moves[0...-1]) +
141
+ algorithm.moves[-1].join_with_cancellation(move, cube_size)
144
142
  end
143
+ end
145
144
 
146
- def self.push_with_cancellation(algorithm, move, cube_size)
147
- raise TypeError unless move.is_a?(AbstractMove)
148
- return Algorithm.move(move) if algorithm.empty?
145
+ def self.push_with_cancellation(algorithm, move, cube_size)
146
+ raise TypeError unless move.is_a?(AbstractMove)
147
+ return Algorithm.move(move) if algorithm.empty?
149
148
 
150
- cancel_variants =
151
- cancel_variants(algorithm).map do |alg|
152
- alg_plus_cancelled_move(alg, move, cube_size)
153
- end
154
- cancel_variants.min_by do |alg|
155
- # QTM is the most sensitive metric, so we use that as the highest priority for
156
- # cancellations.
157
- # We use HTM as a second priority to make sure something like RR still gets merged into
158
- # R2.
159
- # We use the length as tertiary priority to make sure rotations get cancelled even if they
160
- # don't change the move count.
161
- [alg.move_count(cube_size, :qtm), alg.move_count(cube_size, :htm), alg.length]
149
+ cancel_variants =
150
+ cancel_variants(algorithm).map do |alg|
151
+ alg_plus_cancelled_move(alg, move, cube_size)
162
152
  end
153
+ cancel_variants.min_by do |alg|
154
+ # QTM is the most sensitive metric, so we use that as the highest priority for
155
+ # cancellations.
156
+ # We use HTM as a second priority to make sure something like RR still gets merged into
157
+ # R2.
158
+ # We use the length as tertiary priority to make sure rotations get cancelled even if they
159
+ # don't change the move count.
160
+ [alg.move_count(cube_size, :qtm), alg.move_count(cube_size, :htm), alg.length]
163
161
  end
164
162
  end
163
+ end
165
164
  end
@@ -56,7 +56,7 @@ module TwistyPuzzles
56
56
  raise ArgumentError unless colors.include?(top_color)
57
57
  raise ArgumentError unless colors.include?(front_color)
58
58
 
59
- # Note: The reason that this is so complicated is that we want it to still work if the
59
+ # NOTE: The reason that this is so complicated is that we want it to still work if the
60
60
  # chirality corner gets exchanged.
61
61
 
62
62
  # Do the obvious and handle opposites of the top and front color so we have no
@@ -5,114 +5,116 @@ require 'twisty_puzzles/algorithm'
5
5
  require 'twisty_puzzles/cube'
6
6
 
7
7
  module TwistyPuzzles
8
-
9
- # Base class for Commutators.
10
- class Commutator
11
- def cancellations(other, cube_size, metric = :htm)
12
- algorithm.cancellations(other.algorithm, cube_size, metric)
13
- end
8
+ # Base class for Commutators.
9
+ class Commutator
10
+ def cancellations(other, cube_size, metric = :htm)
11
+ algorithm.cancellations(other.algorithm, cube_size, metric)
14
12
  end
13
+ end
15
14
 
16
- # Algorithm that is used like a commutator but actually isn't one.
17
- class FakeCommutator < Commutator
18
- def initialize(algorithm)
19
- raise ArgumentError unless algorithm.is_a?(Algorithm)
20
-
21
- @algorithm = algorithm
22
- end
15
+ # Algorithm that is used like a commutator but actually isn't one.
16
+ class FakeCommutator < Commutator
17
+ def initialize(algorithm)
18
+ raise ArgumentError unless algorithm.is_a?(Algorithm)
23
19
 
24
- attr_reader :algorithm
20
+ super()
21
+ @algorithm = algorithm
22
+ end
25
23
 
26
- def eql?(other)
27
- self.class.equal?(other.class) && @algorithm == other.algorithm
28
- end
24
+ attr_reader :algorithm
29
25
 
30
- alias == eql?
26
+ def eql?(other)
27
+ self.class.equal?(other.class) && @algorithm == other.algorithm
28
+ end
31
29
 
32
- def hash
33
- @hash ||= [self.class, @algorithm].hash
34
- end
30
+ alias == eql?
35
31
 
36
- def inverse
37
- FakeCommutator.new(@algorithm.inverse)
38
- end
32
+ def hash
33
+ @hash ||= [self.class, @algorithm].hash
34
+ end
39
35
 
40
- def to_s
41
- @algorithm.to_s
42
- end
36
+ def inverse
37
+ FakeCommutator.new(@algorithm.inverse)
43
38
  end
44
39
 
45
- # Pure commutator of the form A B A' B'.
46
- class PureCommutator < Commutator
47
- def initialize(first_part, second_part)
48
- raise ArgumentError unless first_part.is_a?(Algorithm)
49
- raise ArgumentError unless second_part.is_a?(Algorithm)
40
+ def to_s
41
+ @algorithm.to_s
42
+ end
43
+ end
50
44
 
51
- @first_part = first_part
52
- @second_part = second_part
53
- end
45
+ # Pure commutator of the form A B A' B'.
46
+ class PureCommutator < Commutator
47
+ def initialize(first_part, second_part)
48
+ raise ArgumentError unless first_part.is_a?(Algorithm)
49
+ raise ArgumentError unless second_part.is_a?(Algorithm)
54
50
 
55
- attr_reader :first_part, :second_part
51
+ super()
52
+ @first_part = first_part
53
+ @second_part = second_part
54
+ end
56
55
 
57
- def eql?(other)
58
- self.class.equal?(other.class) && @first_part == other.first_part &&
59
- @second_part == other.second_part
60
- end
56
+ attr_reader :first_part, :second_part
61
57
 
62
- alias == eql?
58
+ def eql?(other)
59
+ self.class.equal?(other.class) && @first_part == other.first_part &&
60
+ @second_part == other.second_part
61
+ end
63
62
 
64
- def hash
65
- @hash ||= [self.class, @first_part, @second_part].hash
66
- end
63
+ alias == eql?
67
64
 
68
- def inverse
69
- PureCommutator.new(second_part, first_part)
70
- end
65
+ def hash
66
+ @hash ||= [self.class, @first_part, @second_part].hash
67
+ end
71
68
 
72
- def to_s
73
- "[#{@first_part}, #{@second_part}]"
74
- end
69
+ def inverse
70
+ PureCommutator.new(second_part, first_part)
71
+ end
75
72
 
76
- def algorithm
77
- first_part + second_part + first_part.inverse + second_part.inverse
78
- end
73
+ def to_s
74
+ "[#{@first_part}, #{@second_part}]"
79
75
  end
80
76
 
81
- # Setup commutator of the form A B A'.
82
- class SetupCommutator < Commutator
83
- def initialize(setup, inner_commutator)
84
- raise ArgumentError, 'Setup move has to be an algorithm.' unless setup.is_a?(Algorithm)
85
- unless inner_commutator.is_a?(Commutator)
86
- raise ArgumentError, 'Inner commutator has to be a commutator.'
87
- end
77
+ def algorithm
78
+ first_part + second_part + first_part.inverse + second_part.inverse
79
+ end
80
+ end
88
81
 
89
- @setup = setup
90
- @inner_commutator = inner_commutator
82
+ # Setup commutator of the form A B A'.
83
+ class SetupCommutator < Commutator
84
+ def initialize(setup, inner_commutator)
85
+ raise ArgumentError, 'Setup move has to be an algorithm.' unless setup.is_a?(Algorithm)
86
+ unless inner_commutator.is_a?(Commutator)
87
+ raise ArgumentError, 'Inner commutator has to be a commutator.'
91
88
  end
92
89
 
93
- attr_reader :setup, :inner_commutator
90
+ super()
91
+ @setup = setup
92
+ @inner_commutator = inner_commutator
93
+ end
94
+
95
+ attr_reader :setup, :inner_commutator
94
96
 
95
- def eql?(other)
96
- self.class.equal?(other.class) && @setup == other.setup &&
97
- @inner_commutator == other.inner_commutator
98
- end
97
+ def eql?(other)
98
+ self.class.equal?(other.class) && @setup == other.setup &&
99
+ @inner_commutator == other.inner_commutator
100
+ end
99
101
 
100
- alias == eql?
102
+ alias == eql?
101
103
 
102
- def hash
103
- @hash ||= [self.class, @setup, @inner_commutator].hash
104
- end
104
+ def hash
105
+ @hash ||= [self.class, @setup, @inner_commutator].hash
106
+ end
105
107
 
106
- def inverse
107
- SetupCommutator.new(setup, @inner_commutator.inverse)
108
- end
108
+ def inverse
109
+ SetupCommutator.new(setup, @inner_commutator.inverse)
110
+ end
109
111
 
110
- def to_s
111
- "[#{@setup} : #{@inner_commutator}]"
112
- end
112
+ def to_s
113
+ "[#{@setup} : #{@inner_commutator}]"
114
+ end
113
115
 
114
- def algorithm
115
- setup + inner_commutator.algorithm + setup.inverse
116
- end
116
+ def algorithm
117
+ setup + inner_commutator.algorithm + setup.inverse
117
118
  end
119
+ end
118
120
  end