예제 #1
0
    def test_row_isrounds(self):
        self.assertTrue(Row(0).is_rounds())
        self.assertTrue(Row(1).is_rounds())
        self.assertTrue(Row(2).is_rounds())

        self.assertTrue(Row('12345678').is_rounds())
        self.assertFalse(Row('21').is_rounds())
예제 #2
0
    def test_row_conjugator(self):
        x = Row('2143')
        y = Row('3412')

        r = Row.conjugator(x, y)
        self.assertEqual(y, ~r * x * r)

        self.assertFalse(Row.conjugator(x, 4))
예제 #3
0
    def test_row_are_conjugate(self):
        conj_class = list(map(Row, ['2143', '3412', '4321']))

        for r1, r2 in itertools.product(conj_class, conj_class):
            self.assertTrue(Row.are_conjugate(r1, r2))

        for r in conj_class:
            self.assertFalse(Row.are_conjugate(r, 4))
예제 #4
0
    def test_row_conjugator(self):
        x = Row('2143')
        y = Row('3412')

        r = Row.conjugator(x, y)
        self.assertEqual(y, ~r * x * r)

        self.assertFalse(Row.conjugator(x, 4))
예제 #5
0
    def test_row_are_conjugate(self):
        conj_class = list(map(Row, ['2143', '3412', '4321']))

        for r1, r2 in itertools.product(conj_class, conj_class):
            self.assertTrue(Row.are_conjugate(r1, r2))

        for r in conj_class:
            self.assertFalse(Row.are_conjugate(r, 4))
예제 #6
0
    def test_row_equals_string_types(self):
        self.assertEqual(Row('123456'), '123456')

        self.assertEqual(Row(b'123456'), b'123456')
        self.assertEqual(Row(u'123456'), u'123456')

        self.assertEqual(bytes(Row('123456')), b'123456')

        try:
            self.assertEqual(unicode(Row('123456')), u'123456')
        except NameError:
            pass
예제 #7
0
    def test_row_block_subscript_bounds(self):
        rb = RowBlock(*[Change(5, pn) for pn in ['3', '1', '5']])

        self.assertRaises(IndexError, lambda: rb[-1])
        self.assertEqual(rb[0], '12345')
        self.assertEqual(rb[3], '32415')
        self.assertRaises(IndexError, lambda: rb[4])

        self.assertRaises(IndexError, lambda: rb.__setitem__(-1, Row(5)))
        rb[0] = Row(5)  # Should succeed
        rb[3] = Row(5)
        self.assertRaises(IndexError, lambda: rb.__setitem__(4, Row(5)))
예제 #8
0
    def test_row_divide_row(self):
        self.assertEqual(Row('642153') / Row('235164'), Row('164325'))
        self.assertEqual(Row('53678421') / Row('425613'), Row('83456721'))
        self.assertEqual(Row('51324') / Row('645231'), Row('624135'))

        r = Row()
        r = r / '623415'
        self.assertEqual(r, '523461')
        r = r / '623415'
        self.assertEqual(r, '623415')
        r = r / '87654321'
        self.assertEqual(r, '87514326')
        self.assertEqual(r.bells, 8)
예제 #9
0
    def test_row_sign(self):
        self.assertEqual(Row().sign(), +1)
        self.assertEqual(Row(1).sign(), +1)
        self.assertEqual(Row(2).sign(), +1)
        self.assertEqual(Row(17).sign(), +1)

        self.assertEqual(Row('234561').sign(), -1)
        self.assertEqual(Row('234516').sign(), +1)
        self.assertEqual(Row('352461').sign(), +1)

        self.assertEqual(Row('312647958').sign(), -1)
        self.assertEqual(Row('167832495').sign(), +1)
예제 #10
0
    def leads(self):
        """
        A list containing the leads of the composition.
        """
        if self._leads is None:

            lead_specifications = []
            for line in self.configs.composition:
                if line in self.configs.methods:
                    lead_specifications.append({
                        'method_name':
                        line,
                        'method_object':
                        self.configs.methods[line],
                        'call_symbol':
                        '',
                        'call_object':
                        None,
                    })
                elif line in self.configs.calls:
                    lead_specifications[-1]['call_symbol'] = line
                    lead_specifications[-1]['call_object'] = \
                        self.configs.calls[line]

            self._leads = []
            lead_head = Row(self.configs.bells)
            for lead_specification in lead_specifications:
                lead_specification['starting_lead_head'] = lead_head
                self._leads.append(Lead(**lead_specification))
                lead_head = self._leads[-1].lead_head

        return self._leads
예제 #11
0
 def test_row_multiply_change(self):
     r = Row()
     r = r * Change(6, 'X')
     self.assertEqual(r, '214365')
     r = r * Change(6, '1')
     self.assertEqual(r, '241635')
     r = r * Change(8, 'X')
     self.assertEqual(r, '42615387')
     r = r * Change(5, '3')
     self.assertEqual(r, '24651387')
예제 #12
0
    def test_group_named_groups(self):
        self.assertEqual(list(Group.symmetric_group(0)), [Row()])
        self.assertEqual(list(Group.symmetric_group(3)),
                         ['123', '132', '213', '231', '312', '321'])
        self.assertEqual(list(Group.symmetric_group(3, 1)),
                         ['1234', '1243', '1324', '1342', '1423', '1432'])
        self.assertEqual(list(Group.symmetric_group(
            3, 1, 5)), ['12345', '12435', '13245', '13425', '14235', '14325'])

        self.assertEqual(list(Group.alternating_group(0)), [Row()])
        self.assertEqual(list(Group.alternating_group(3)),
                         ['123', '231', '312'])
        self.assertEqual(list(Group.alternating_group(3, 1)),
                         ['1234', '1342', '1423'])
        self.assertEqual(list(Group.alternating_group(3, 1, 5)),
                         ['12345', '13425', '14235'])

        self.assertRaises(ValueError, lambda: Group.symmetric_group(2, 2, 3))
        self.assertRaises(ValueError, lambda: Group.alternating_group(2, 2, 3))
예제 #13
0
 def test_row_cycles(self):
     self.assertEqual(Row().cycles(), '')
     self.assertEqual(Row(1).cycles(), '1')
     self.assertEqual(Row(5).cycles(), '1,2,3,4,5')
     self.assertEqual(Row('124536').cycles(), '1,2,345,6')
     self.assertEqual(Row('214563').cycles(), '12,3456')
     self.assertEqual(Row('2145673').cycles(), '12,34567')
예제 #14
0
 def test_row_order(self):
     self.assertEqual(Row().order(), 1)
     self.assertEqual(Row(1).order(), 1)
     self.assertEqual(Row('21').order(), 2)
     self.assertEqual(Row('234561').order(), 6)
     self.assertEqual(Row('21436578').order(), 2)
     self.assertEqual(Row('2315674').order(), 12)
예제 #15
0
    def is_cyclic(self):
        """
        Whether the composition has a cyclic part end.
        (Returns False if it's a single-part composition).
        """

        if self._is_cyclic is None:
            # Rows don't have an is_cyclic method (why not?)
            # Generate all cyclic part ends and see if ours is one of them.
            if self.is_treble_fixed:
                cyclic_part_ends = [
                    Row.cyclic(self.configs.bells, 1, n)
                    for n in range(self.configs.bells - 1)
                ]
            else:
                cyclic_part_ends = [
                    Row.cyclic(self.configs.bells, 0, n)
                    for n in range(self.configs.bells)
                ]

            self._is_cyclic = self.part_end in cyclic_part_ends

        return self.parts != 1 and self._is_cyclic
예제 #16
0
    def create_worksheet(self, workbook, name, landscape=False):
        """
        Creates a worksheet and fills it with rows.
        """
        worksheet = workbook.add_worksheet(name)

        if landscape:
            worksheet.set_landscape()
        worksheet.set_paper(9)  # A4
        worksheet.set_margins(0.4, 0.4, 0.4, 0.4)  # 1cm all round
        worksheet.set_header('', {'margin': 0})
        worksheet.set_footer('', {'margin': 0})
        worksheet.fit_to_pages(1, 1)

        # Start with the smallest possible rows (i.e. length of longest method)
        # and increase it until we pass the required aspect ratio
        rows = max(
            [lead.method_object.length for lead in self.composition.leads])
        desired_aspect_ratio = 1 / sqrt(2) if landscape else sqrt(2)
        while self.calculate_aspect_ratio(rows) < desired_aspect_ratio:
            rows += 2

        # Stamp leads all over the worksheet
        self.row_index = 0
        self.col_index = 0
        self.lead_head = Row(self.composition.configs.bells)
        col_index_delta = self.composition.configs.bells + 3
        for lead in self.composition.leads:
            if self.row_index + lead.method_object.length >= rows:
                self.row_index = 0
                self.col_index += col_index_delta
            self.print_lead(lead, worksheet)

        # Set print area and column widths
        worksheet.print_area(
            0, 0, rows,
            self.calculate_columns(rows) * col_index_delta - 2)
        for i in range(self.calculate_columns(rows)):
            worksheet.set_column(i * col_index_delta,
                                 (i + 1) * col_index_delta - 3,
                                 BELL_COLUMN_WIDTH)
            worksheet.set_column((i + 1) * col_index_delta - 1,
                                 (i + 1) * col_index_delta - 1,
                                 GUTTER_COLUMN_WIDTH)
예제 #17
0
 def test_group_iterator(self):
     self.assertEqual(list(Group()), [Row()])
     self.assertEqual(list(Group(6)), ['123456'])
     self.assertEqual(list(Group('134256')), ['123456', '134256', '142356'])
     self.assertEqual(list(Group('213564', '123546')), [
         '123456',
         '123465',
         '123546',
         '123564',
         '123645',
         '123654',
         '213456',
         '213465',
         '213546',
         '213564',
         '213645',
         '213654',
     ])
     self.assertEqual(list(Group('654321')), ['123456', '654321'])
예제 #18
0
    def test_group_coset_labels(self):
        G = Group('2143', '1324', '1243')  # S_4
        H = Group('2143', '1324')  # Rows in plain hunt

        # A row in G but not H; this must be in a coset of H.
        # This is the lexicographically lowest row (except rounds), so should be
        # the label of its coset.
        g = Row('1243')

        for h_dash in G:
            # gH is the left coset of H in G with respect to g
            lcoset_row = g * h_dash

            # Hg is the right coset of H in G with respect to g
            rcoset_row = h_dash * g

            if h_dash in H:
                self.assertEqual(H.lcoset_label(lcoset_row), g)
                self.assertEqual(H.rcoset_label(rcoset_row), g)
            else:
                self.assertNotEqual(H.lcoset_label(lcoset_row), g)
                self.assertNotEqual(H.rcoset_label(rcoset_row), g)
예제 #19
0
    def test_row_find(self):
        r = Row('615423')

        self.assertEqual(r.find(0), 1)
        self.assertEqual(r.find(1), 4)
        self.assertEqual(r.find(2), 5)
        self.assertEqual(r.find(3), 3)
        self.assertEqual(r.find(4), 2)
        self.assertEqual(r.find(5), 0)
        self.assertEqual(r.find(6), 6)

        self.assertRaises(ValueError, lambda: r.find(-1))
        r.find(0)
        r.find(MAX_BELL_NUMBER)
        self.assertRaises(ValueError, lambda: r.find(MAX_BELL_NUMBER + 1))
예제 #20
0
    def test_row_multiply_row(self):
        self.assertEqual(Row('4312') * Row('2341'), Row('3124'))
        self.assertEqual(Row('7631425') * Row('2347165'), Row('6315724'))
        self.assertEqual(Row('12387654') * Row('631245'), Row('63128754'))
        self.assertEqual(Row('24531') * Row('57863124'), Row('17865243'))

        r = Row()
        r = r * '34512'
        self.assertEqual(r, '34512')
        r = r * '14253'
        self.assertEqual(r, '31425')
예제 #21
0
 def test_row_subscript_bounds(self):
     r = Row(6)
     self.assertRaises(IndexError, lambda: r[-1])
     self.assertEqual(r[0], 0)
     self.assertEqual(r[5], 5)
     self.assertRaises(IndexError, lambda: r[6])
예제 #22
0
 def test_row_inverse_tilde(self):
     self.assertEqual(~Row('654321'), '654321')
     self.assertEqual(~Row('312'), '231')
     self.assertEqual(~Row('18234567'), '13456782')
예제 #23
0
 def test_row_constructor_exceptions(self):
     self.assertRaises(ValueError, lambda: Row(-1))
     Row(0)
     Row(MAX_BELL_NUMBER)
     self.assertRaises(ValueError, lambda: Row(MAX_BELL_NUMBER + 1))
     self.assertRaises(TypeError, lambda: Row(self))
예제 #24
0
def bell_number_to_char(num):
    """
    Converts a bell number into the character that represents it.
    """
    return str(Row(num + 1))[num]  # Hack this out of a Row string
예제 #25
0
    def test_group_conjugate(self):
        G = Group('2143', '1324')
        r = Row('1243')

        self.assertEqual(sorted([~r * g * r for g in G]),
                         sorted(G.conjugate(r)))
예제 #26
0
 def test_row_bells(self):
     self.assertEqual(Row().bells, 0)
     self.assertEqual(Row(7).bells, 7)
     self.assertEqual(Row('12345').bells, 5)
예제 #27
0
    def test_row_named_rows(self):
        self.assertEqual(Row.rounds(0), '')
        self.assertEqual(Row.rounds(1), '1')
        self.assertEqual(Row.rounds(2), '12')
        self.assertEqual(Row.rounds(5), '12345')
        self.assertEqual(Row.rounds(8), '12345678')

        self.assertEqual(Row.queens(0), '')
        self.assertEqual(Row.queens(1), '1')
        self.assertEqual(Row.queens(2), '12')
        self.assertEqual(Row.queens(5), '13524')
        self.assertEqual(Row.queens(8), '13572468')

        self.assertEqual(Row.kings(0), '')
        self.assertEqual(Row.kings(1), '1')
        self.assertEqual(Row.kings(2), '12')
        self.assertEqual(Row.kings(5), '53124')
        self.assertEqual(Row.kings(8), '75312468')

        self.assertEqual(Row.tittums(0), '')
        self.assertEqual(Row.tittums(1), '1')
        self.assertEqual(Row.tittums(2), '12')
        self.assertEqual(Row.tittums(5), '14253')
        self.assertEqual(Row.tittums(8), '15263748')

        self.assertEqual(Row.reverse_rounds(0), '')
        self.assertEqual(Row.reverse_rounds(1), '1')
        self.assertEqual(Row.reverse_rounds(2), '21')
        self.assertEqual(Row.reverse_rounds(5), '54321')
        self.assertEqual(Row.reverse_rounds(8), '87654321')
예제 #28
0
    def test_operators_return_not_implemented(self):
        # Arithmetic operator returns NotImplemented when given unknown types
        self.assertEqual(Row().__lt__(self), NotImplemented)
        self.assertEqual(Row().__le__(self), NotImplemented)
        self.assertEqual(Row().__eq__(self), NotImplemented)
        self.assertEqual(Row().__ne__(self), NotImplemented)
        self.assertEqual(Row().__gt__(self), NotImplemented)
        self.assertEqual(Row().__ge__(self), NotImplemented)
        self.assertEqual(Row().__mul__(self), NotImplemented)
        self.assertEqual(Row().__rmul__(self), NotImplemented)
        self.assertEqual(Row().__truediv__(self), NotImplemented)
        self.assertEqual(Row().__rtruediv__(self), NotImplemented)

        # ... but passes through errors parsing known types.
        self.assertRaises(ValueError, lambda: Row().__lt__('!'))
        self.assertRaises(ValueError, lambda: Row().__le__('!'))
        self.assertRaises(ValueError, lambda: Row().__eq__('!'))
        self.assertRaises(ValueError, lambda: Row().__ne__('!'))
        self.assertRaises(ValueError, lambda: Row().__gt__('!'))
        self.assertRaises(ValueError, lambda: Row().__ge__('!'))
        self.assertRaises(ValueError, lambda: Row().__mul__('!'))
        self.assertRaises(ValueError, lambda: Row().__rmul__('!'))
        self.assertRaises(ValueError, lambda: Row().__truediv__('!'))
        self.assertRaises(ValueError, lambda: Row().__rtruediv__('!'))

        try:
            self.assertEqual(Row().__div__(self), NotImplemented)
            self.assertRaises(ValueError, lambda: Row().__div__('!'))
        except AttributeError:
            pass  # Ignore (Python 3 doesn't create __div__)
예제 #29
0
    def test_row_find(self):
        r = Row('615423')

        self.assertEqual(r.find(0), 1)
        self.assertEqual(r.find(1), 4)
        self.assertEqual(r.find(2), 5)
        self.assertEqual(r.find(3), 3)
        self.assertEqual(r.find(4), 2)
        self.assertEqual(r.find(5), 0)
        self.assertEqual(r.find(6), 6)

        self.assertRaises(ValueError, lambda: r.find(-1))
        r.find(0)
        r.find(MAX_BELL_NUMBER)
        self.assertRaises(ValueError, lambda: r.find(MAX_BELL_NUMBER + 1))
        self.assertRaises(TypeError, lambda: r.find(self))

        self.assertEqual(r.find(Bell(0)), 1)
        self.assertEqual(r.find(Bell('1')), 1)
예제 #30
0
 def test_row_repr(self):
     self.assertEqual(repr(Row()), 'Row()')
     self.assertEqual(repr(Row('123456')), "Row('123456')")
예제 #31
0
 def test_row_print(self):
     self.assertEqual(str(Row()), '')
     self.assertEqual(str(Row('123456')), '123456')
예제 #32
0
    def test_row_equals(self):
        self.assertTrue(Row() == Row(''))
        self.assertTrue(Row() != Row('1'))
        self.assertTrue(Row(6) == Row('123456'))

        self.assertTrue(Row('123456') == Row('123456'))
        self.assertFalse(Row('123456') != Row('123456'))

        self.assertTrue(Row('123456') != Row('123465'))
        self.assertFalse(Row('123456') == Row('123465'))

        self.assertTrue(Row('123456') != Row('1234'))
        self.assertFalse(Row('123456') == Row('1234'))

        self.assertTrue(Row('123456') != Row('12345678'))
        self.assertFalse(Row('123456') == Row('12345678'))
예제 #33
0
    def execute(self):
        if self.composition.configs.has_config('calls'):
            longest_call = max(map(
                lambda s: len(s),
                self.composition.configs.calls.keys()
            ))
            format_string = '  {0.method_name}\n{0.call_symbol:' + \
                str(longest_call) + '} {0.lead_head}'
        else:
            format_string = '  {0.method_name}\n{0.lead_head}'

        file = os.path.join(self.get_output_directory(), 'composition.txt')
        with open(file, 'w') as file:

            def output(*args):
                """Prints a line to the output file without any newline."""
                print(*args, end='', file=file)

            # Title
            output('{length} {stage}\n'.format(
                length=self.composition.length,
                stage=Method.stage_name(self.composition.configs.bells),
            ))

            # Number of methods
            methods = {}
            for name, length in self.composition.method_balance.items():
                # Assemble dict mapping lengths to lists of methods
                # e.g. {176: ['Slinky'], 880: ['Maypole']}
                if length not in methods:
                    methods[length] = []
                methods[length].append(name)

            for length in methods.keys():
                # Replace each list of methods with string for output
                # e.g. {176: '176 Slinky', 880: '880 Maypole'}
                methods[length] = ', '.join(sorted(methods[length]))
                methods[length] = '{0} {1}'.format(length, methods[length])

            methods = [
                # Sort entries in reverse order of length
                # e.g. {880: '880 Maypole', 176: '176 Slinky'}
                methods[length] for length
                in reversed(sorted(methods.keys()))
            ]

            output('({number}m: {methods})\n'.format(
                number=len(self.composition.method_balance),
                methods='; '.join(methods),
            ))
            output('\n')

            # Rows of the composition
            if self.composition.configs.has_config('calls'):
                output(' ' * (longest_call + 1))

            output(Row(self.composition.configs.bells))

            for lead in self.composition.leads:
                output(format_string.format(lead))

            output('\n')
            output('\n')

            # Composition statistics
            output('{0.parts} part. {0.com} com.\n'.format(self.composition))