<commit_before>import numpy as np
import re

class WheelGeometry:
    'Geometric parameters including size and lacing pattern'

    def __str__(self):
        return ("Bicycle wheel geometry object\n"
                "  rim diameter        :{rd:6.1f} mm\n"
                "  hub diameter        :{hd1:6.1f} mm\n"
                "  hub diameter (drive):{hd2:6.1f} mm\n"
                "  hub width           :{hw1:6.1f} mm\n"
                "  hub width (drive)   :{hw2:6.1f} mm\n"
                "  number of spokes    :{ns:6d}\n")\
            .format(rd=self.d_rim*1000,
                    hd1=self.d1_hub*1000,
                    hd2=self.d2_hub*1000,
                    hw1=self.w1_hub*1000,
                    hw2=self.w2_hub*1000,
                    ns=len(self.lace_hub_n))

    def sort_spokes(self):
        'Sort spokes by rim node'

        self.lace_hub_n = self.lace_hub_n[self.lace_rim_n.argsort()]
        self.lace_rim_n.sort()

    def parse_wheel_file(self, fname):

        def keyword_rim(l, f):

            # Default values
            args = {'diam': 0.6, 'spacing': 'default', 'N': 36}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            self.d_rim = float(args['diam'])
            N = int(args['N'])
            self.n_rim_nodes = N

            if args['spacing'] == 'default':

                # Space rim nodes evenly
                self.a_rim_nodes = np.linspace(0, 2*np.pi * (N-1)/N, N)
                l = f.readline()

            elif args['spacing'] == 'custom':

                # Read rim node positions from input file
                l = f.readline()

                self.a_rim_nodes = np.zeros(N, dtype=np.float)
                while len(l) > 0:

                    l_rim_node = l.strip().split()

                    if re.match('^\d+$', l_rim_node[0]):  # check if line is an integer
                        self.a_rim_nodes[int(l_rim_node[0]) - 1] = np.radians(float(l_rim_node[1]))
                        l = f.readline()
                    elif l_rim_node[0][0] == '#':
                        l = f.readline()
                    else:
                        break

                # Make sure all angles are unique
                if not len(self.a_rim_nodes) == len(set(self.a_rim_nodes)):
                    raise ValueError('When using spacing=custom, all node angles must be defined')

            else:
                raise ValueError('Invalid value for parameter \'spacing\': ' + args['spacing'])

            # Increment to the next line
            return l

        def keyword_hub(l, f):

            # Default values
            args = {'diam': 0.6, 'diam_drive': None, 'width': 0.035, 'width_drive': None, 
                    'spacing': 'default', 'N': 36}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            self.d1_hub = float(args['diam'])
            self.d2_hub = self.d1_hub
            if args['diam_drive'] is not None:
                self.d2_hub = float(args['diam_drive'])

            self.w1_hub = float(args['width'])
            self.w2_hub = self.w1_hub
            if args['width_drive'] is not None:
                self.w2_hub = float(args['width_drive'])

            N = int(args['N'])
            self.n_hub_nodes = N
            self.s_hub_nodes = np.zeros(N, dtype=np.int8)

            if args['spacing'] == 'default':

                # Space rim nodes evenly
                self.a_hub_nodes = np.linspace(0, 2*np.pi * (N-1)/N, N)
                self.s_hub_nodes[::2] = 1    # Drive-side nodes
                self.s_hub_nodes[1::2] = -1  # Left-side nodes
                l = f.readline()

            elif args['spacing'] == 'custom':

                # Read hub node positions from input file
                l = f.readline()

                self.a_hub_nodes = np.zeros(N, dtype=np.float)

                while len(l) > 0:

                    l_hub_node = l.strip().split()

                    if re.match('^\d+$', l_hub_node[0]):  # check if line is an integer
                        i_node = int(l_hub_node[0]) - 1
                        self.a_hub_nodes[i_node] = np.radians(float(l_hub_node[1]))

                        if len(l_hub_node) > 2 and l_hub_node[2].upper() == 'D':
                            self.s_hub_nodes[i_node] = 1  # Drive-side node
                        else:
                            self.s_hub_nodes[i_node] = -1  # Left-side node

                        l = f.readline()
                    elif l_hub_node[0][0] == '#':
                        l = f.readline()
                    else:
                        break

                # Make sure all angles are unique
                if not len(self.a_hub_nodes) == len(set(self.a_hub_nodes)):
                    raise ValueError('When using spacing=custom, all node angles must be defined')

            else:
                raise ValueError('Invalid value for parameter \'spacing\': ' + args['spacing'])

            # Increment to the next line
            return l

        def keyword_spokes(l, f):
            # Default values
            args = {'pattern': 'default'}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            if args['pattern'] == 'radial':
                self.lace_radial()

            if args['pattern'] == 'cross1':
                self.lace_cross(1)
            if args['pattern'] == 'cross2':
                self.lace_cross(2)
            if args['pattern'] == 'cross3':
                self.lace_cross(3)

            if args['pattern'] == 'custom':

                # Read hub node positions from input file<commit_msg>file parser supports x4 lacing<commit_after>import numpy as np
import re

class WheelGeometry:
    'Geometric parameters including size and lacing pattern'

    def __str__(self):
        return ("Bicycle wheel geometry object\n"
                "  rim diameter        :{rd:6.1f} mm\n"
                "  hub diameter        :{hd1:6.1f} mm\n"
                "  hub diameter (drive):{hd2:6.1f} mm\n"
                "  hub width           :{hw1:6.1f} mm\n"
                "  hub width (drive)   :{hw2:6.1f} mm\n"
                "  number of spokes    :{ns:6d}\n")\
            .format(rd=self.d_rim*1000,
                    hd1=self.d1_hub*1000,
                    hd2=self.d2_hub*1000,
                    hw1=self.w1_hub*1000,
                    hw2=self.w2_hub*1000,
                    ns=len(self.lace_hub_n))

    def sort_spokes(self):
        'Sort spokes by rim node'

        self.lace_hub_n = self.lace_hub_n[self.lace_rim_n.argsort()]
        self.lace_rim_n.sort()

    def parse_wheel_file(self, fname):

        def keyword_rim(l, f):

            # Default values
            args = {'diam': 0.6, 'spacing': 'default', 'N': 36}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            self.d_rim = float(args['diam'])
            N = int(args['N'])
            self.n_rim_nodes = N

            if args['spacing'] == 'default':

                # Space rim nodes evenly
                self.a_rim_nodes = np.linspace(0, 2*np.pi * (N-1)/N, N)
                l = f.readline()

            elif args['spacing'] == 'custom':

                # Read rim node positions from input file
                l = f.readline()

                self.a_rim_nodes = np.zeros(N, dtype=np.float)
                while len(l) > 0:

                    l_rim_node = l.strip().split()

                    if re.match('^\d+$', l_rim_node[0]):  # check if line is an integer
                        self.a_rim_nodes[int(l_rim_node[0]) - 1] = np.radians(float(l_rim_node[1]))
                        l = f.readline()
                    elif l_rim_node[0][0] == '#':
                        l = f.readline()
                    else:
                        break

                # Make sure all angles are unique
                if not len(self.a_rim_nodes) == len(set(self.a_rim_nodes)):
                    raise ValueError('When using spacing=custom, all node angles must be defined')

            else:
                raise ValueError('Invalid value for parameter \'spacing\': ' + args['spacing'])

            # Increment to the next line
            return l

        def keyword_hub(l, f):

            # Default values
            args = {'diam': 0.6, 'diam_drive': None, 'width': 0.035, 'width_drive': None, 
                    'spacing': 'default', 'N': 36}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            self.d1_hub = float(args['diam'])
            self.d2_hub = self.d1_hub
            if args['diam_drive'] is not None:
                self.d2_hub = float(args['diam_drive'])

            self.w1_hub = float(args['width'])
            self.w2_hub = self.w1_hub
            if args['width_drive'] is not None:
                self.w2_hub = float(args['width_drive'])

            N = int(args['N'])
            self.n_hub_nodes = N
            self.s_hub_nodes = np.zeros(N, dtype=np.int8)

            if args['spacing'] == 'default':

                # Space rim nodes evenly
                self.a_hub_nodes = np.linspace(0, 2*np.pi * (N-1)/N, N)
                self.s_hub_nodes[::2] = 1    # Drive-side nodes
                self.s_hub_nodes[1::2] = -1  # Left-side nodes
                l = f.readline()

            elif args['spacing'] == 'custom':

                # Read hub node positions from input file
                l = f.readline()

                self.a_hub_nodes = np.zeros(N, dtype=np.float)

                while len(l) > 0:

                    l_hub_node = l.strip().split()

                    if re.match('^\d+$', l_hub_node[0]):  # check if line is an integer
                        i_node = int(l_hub_node[0]) - 1
                        self.a_hub_nodes[i_node] = np.radians(float(l_hub_node[1]))

                        if len(l_hub_node) > 2 and l_hub_node[2].upper() == 'D':
                            self.s_hub_nodes[i_node] = 1  # Drive-side node
                        else:
                            self.s_hub_nodes[i_node] = -1  # Left-side node

                        l = f.readline()
                    elif l_hub_node[0][0] == '#':
                        l = f.readline()
                    else:
                        break

                # Make sure all angles are unique
                if not len(self.a_hub_nodes) == len(set(self.a_hub_nodes)):
                    raise ValueError('When using spacing=custom, all node angles must be defined')

            else:
                raise ValueError('Invalid value for parameter \'spacing\': ' + args['spacing'])

            # Increment to the next line
            return l

        def keyword_spokes(l, f):
            # Default values
            args = {'pattern': 'default'}

            # Parse arguments
            l_args = l.strip().split()
            for c in l_args[1::]:
                # Set option
                key = c.split('=')[0]
                arg = c.split('=')[1]
                if key in args:
                    args[key] = arg
                else:
                    raise ValueError('Invalid parameter: {:s}'.format(key))

            if args['pattern'] == 'radial':
                self.lace_radial()

            if args['pattern'] == 'cross1':
                self.lace_cross(1)
            if args['pattern'] == 'cross2':
                self.lace_cross(2)
            if args['pattern'] == 'cross3':
                self.lace_cross(3)
            if args['pattern'] == 'cross4':
                self.lace_cross(4)

            if args['pattern'] == 'custom':

                # Read hub node positions from input file
{'fraction_non_alphanumeric': 0.12229453609807779, 'fraction_numerical': 0.01657333131527168, 'mean_word_length': 1.260906757912746, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6862497', 'n_tokens_mistral': 4130, 'n_tokens_neox': 3768, 'n_words': 893}