Create a construction table for a Zmatrix.

        A construction table is basically a Zmatrix without the values
        for the bond lengths, angles and dihedrals.
        It contains the whole information about which reference atoms
        are used by each atom in the Zmatrix.

        The absolute references in cartesian space are one of the following
        magic strings::

            ['origin', 'e_x', 'e_y', 'e_z']

        This method creates a so called "chemical" construction table,
        which makes use of the connectivity table in this molecule.

        Args:
            fragment_list (sequence): There are four possibilities to specify
                the sequence of fragments:

                1. A list of tuples is given. Each tuple contains the fragment
                with its corresponding construction table in the form of::

                    [(frag1, c_table1), (frag2, c_table2)...]

                If the construction table of a fragment is not complete,
                the rest of each fragment's
                construction table is calculated automatically.

                2. It is possible to omit the construction tables for some
                or all fragments as in the following example::

                    [(frag1, c_table1), frag2, (frag3, c_table3)...]

                3. If ``self`` contains more atoms than the union over all
                fragments, the rest of the molecule without the fragments
                is automatically prepended using
                :meth:`~Cartesian.get_without`::

                    self.get_without(fragments) + fragment_list

                4. If fragment_list is ``None`` then fragmentation, etc.
                is done automatically. The fragments are then sorted by
                their number of atoms, in order to use the largest fragment
                as reference for the other ones.

            use_lookup (bool): Use a lookup variable for
                :meth:`~chemcoord.Cartesian.get_bonds`. The default is
                specified in ``settings['defaults']['use_lookup']``
            perform_checks (bool): The checks for invalid references are
                performed using :meth:`~chemcoord.Cartesian.correct_dihedral`
                and :meth:`~chemcoord.Cartesian.correct_absolute_refs`.

        Returns:
            :class:`pandas.DataFrame`: Construction table

def get_construction_table(self, fragment_list=None,
                               use_lookup=None,
                               perform_checks=True):
        """Create a construction table for a Zmatrix.

        A construction table is basically a Zmatrix without the values
        for the bond lengths, angles and dihedrals.
        It contains the whole information about which reference atoms
        are used by each atom in the Zmatrix.

        The absolute references in cartesian space are one of the following
        magic strings::

            ['origin', 'e_x', 'e_y', 'e_z']

        This method creates a so called "chemical" construction table,
        which makes use of the connectivity table in this molecule.

        Args:
            fragment_list (sequence): There are four possibilities to specify
                the sequence of fragments:

                1. A list of tuples is given. Each tuple contains the fragment
                with its corresponding construction table in the form of::

                    [(frag1, c_table1), (frag2, c_table2)...]

                If the construction table of a fragment is not complete,
                the rest of each fragment's
                construction table is calculated automatically.

                2. It is possible to omit the construction tables for some
                or all fragments as in the following example::

                    [(frag1, c_table1), frag2, (frag3, c_table3)...]

                3. If ``self`` contains more atoms than the union over all
                fragments, the rest of the molecule without the fragments
                is automatically prepended using
                :meth:`~Cartesian.get_without`::

                    self.get_without(fragments) + fragment_list

                4. If fragment_list is ``None`` then fragmentation, etc.
                is done automatically. The fragments are then sorted by
                their number of atoms, in order to use the largest fragment
                as reference for the other ones.

            use_lookup (bool): Use a lookup variable for
                :meth:`~chemcoord.Cartesian.get_bonds`. The default is
                specified in ``settings['defaults']['use_lookup']``
            perform_checks (bool): The checks for invalid references are
                performed using :meth:`~chemcoord.Cartesian.correct_dihedral`
                and :meth:`~chemcoord.Cartesian.correct_absolute_refs`.

        Returns:
            :class:`pandas.DataFrame`: Construction table
        """
        if use_lookup is None:
            use_lookup = settings['defaults']['use_lookup']

        if fragment_list is None:
            self.get_bonds(use_lookup=use_lookup)
            self._give_val_sorted_bond_dict(use_lookup=use_lookup)
            fragments = sorted(self.fragmentate(use_lookup=use_lookup),
                               key=len, reverse=True)
            # During function execution the bonding situation does not change,
            # so the lookup may be used now.
            use_lookup = True
        else:
            fragments = fragment_list

        def prepend_missing_parts_of_molecule(fragment_list):
            for fragment in fragment_list:
                if pd.api.types.is_list_like(fragment):
                    try:
                        full_index |= fragment[0].index
                    except NameError:
                        full_index = fragment[0].index
                else:
                    try:
                        full_index |= fragment.index
                    except NameError:
                        full_index = fragment.index

            if not self.index.difference(full_index).empty:
                missing_part = self.get_without(self.loc[full_index],
                                                use_lookup=use_lookup)
                fragment_list = missing_part + fragment_list
            return fragment_list

        fragments = prepend_missing_parts_of_molecule(fragments)

        if isinstance(fragments[0], tuple):
            fragment, references = fragments[0]
            full_table = fragment._get_frag_constr_table(
                use_lookup=use_lookup, predefined_table=references)
        else:
            fragment = fragments[0]
            full_table = fragment._get_frag_constr_table(use_lookup=use_lookup)

        for fragment in fragments[1:]:
            finished_part = self.loc[full_table.index]
            if pd.api.types.is_list_like(fragment):
                fragment, references = fragment
                if len(references) < min(3, len(fragment)):
                    raise ValueError('If you specify references for a '
                                     'fragment, it has to consist of at least'
                                     'min(3, len(fragment)) rows.')
                constr_table = fragment._get_frag_constr_table(
                    predefined_table=references, use_lookup=use_lookup)
            else:
                i, b = fragment.get_shortest_distance(finished_part)[:2]
                constr_table = fragment._get_frag_constr_table(
                    start_atom=i, use_lookup=use_lookup)
                if len(full_table) == 1:
                    a, d = 'e_z', 'e_x'
                elif len(full_table) == 2:
                    if b == full_table.index[0]:
                        a = full_table.index[1]
                    else:
                        a = full_table.index[0]
                    d = 'e_x'
                else:
                    if b in full_table.index[:2]:
                        if b == full_table.index[0]:
                            a = full_table.index[2]
                            d = full_table.index[1]
                        else:
                            a = full_table.loc[b, 'b']
                            d = full_table.index[2]
                    else:
                        a, d = full_table.loc[b, ['b', 'a']]

                if len(constr_table) >= 1:
                    constr_table.iloc[0, :] = b, a, d
                if len(constr_table) >= 2:
                    constr_table.iloc[1, [1, 2]] = b, a
                if len(constr_table) >= 3:
                    constr_table.iloc[2, 2] = b

            full_table = pd.concat([full_table, constr_table])

        c_table = full_table
        if perform_checks:
            c_table = self.correct_dihedral(c_table)
            c_table = self.correct_dihedral(c_table, use_lookup=use_lookup)
            c_table = self.correct_absolute_refs(c_table)
        return c_table