/// <summary>
        /// Updates an instance of <see cref="FermionHamiltonian"/>
        /// with all spin-orbitals from described by a sequence of two-body orbital integrals.
        /// </summary>
        /// <param name="nOrbitals">Total number of distinct orbitals.</param>
        /// <param name="hpqTerms">Sequence of two-body orbital integrals.</param>
        /// <param name="hamiltonian">Fermion Hamiltonian to be updated.</param>
        public void CreateTwoBodySpinOrbitalTerms(OrbitalIntegral orbitalIntegral)
        {
            // One-electron orbital integral symmetries
            // ij = ji
            var pqSpinOrbitals = orbitalIntegral.EnumerateOrbitalSymmetries().EnumerateSpinOrbitals();

            var coefficient = orbitalIntegral.Coefficient;

            foreach (var pq in pqSpinOrbitals)
            {
                var p = pq[0];
                var q = pq[1];

                var pInt = p.ToInt();
                var qInt = q.ToInt();
                if (pInt == qInt)
                {
                    AddFermionTerm(FermionTermType.Common.PPTermType, new FermionTerm {
                        CreationAnnihilationIndices = new Int64[] { 1, 0 }, SpinOrbitalIndices = pq, coeff = coefficient
                    });
                }
                else if (pInt < qInt)
                {
                    AddFermionTerm(FermionTermType.Common.PQTermType, new FermionTerm {
                        CreationAnnihilationIndices = new Int64[] { 1, 0 }, SpinOrbitalIndices = pq, coeff = 2.0 * coefficient
                    });
                }
            }
        }
        /// <summary>
        /// Updates an instance of <see cref="FermionHamiltonian"/>
        /// with all spin-orbitals from described by a sequence of four-body orbital integrals.
        /// </summary>
        /// <param name="nOrbitals">Total number of distinct orbitals.</param>
        /// <param name="rawPQRSTerms">Sequence of four-body orbital integrals.</param>
        /// <param name="hamiltonian">Fermion Hamiltonian to be updated.</param>
        public void CreateFourBodySpinOrbitalTerms(OrbitalIntegral orbitalIntegral)
        {
            // Two-electron orbital integral symmetries
            // ijkl = lkji = jilk = klij = ikjl = ljki = kilj = jlik.
            var pqrsSpinOrbitals = orbitalIntegral.EnumerateOrbitalSymmetries().EnumerateSpinOrbitals();
            var coefficient      = orbitalIntegral.Coefficient;


            // We only need to see one of these.
            // Now iterate over pqrsArray
            foreach (var pqrs in pqrsSpinOrbitals)
            {
                var p = pqrs[0];
                var q = pqrs[1];
                var r = pqrs[2];
                var s = pqrs[3];

                var pInt = p.ToInt();
                var qInt = q.ToInt();
                var rInt = r.ToInt();
                var sInt = s.ToInt();

                // Only consider terms on the lower diagonal due to Hermitian symmetry.

                // For terms with two different orbital indices, possibilities are
                // PPQQ (QQ = 0), PQPQ, QPPQ (p<q), PQQP, QPQP (p<q), QQPP (PP=0)
                // Hence, if we only count PQQP, and PQPQ, we need to double the coefficient.
                // iU jU jU iU | iU jD jD iD | iD jU jU iD | iD jD jD iD
                if (pInt == sInt && qInt == rInt && pInt < qInt)
                {   // PQQP
                    AddFermionTerm(FermionTermType.Common.PQQPTermType, new FermionTerm {
                        CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, r, s }, coeff = 1.0 * coefficient
                    });
                }
                else if (pInt == rInt && qInt == sInt && pInt < qInt)
                {
                    // iU jU iU jU | iD jD iD jD
                    // PQPQ
                    AddFermionTerm(FermionTermType.Common.PQQPTermType, new FermionTerm {
                        CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, s, r }, coeff = -1.0 * coefficient
                    });
                }
                else if (qInt == rInt && pInt < sInt && rInt != sInt && pInt != qInt)
                {
                    // PQQR
                    // For any distinct pqr, [i;j;j;k] generates PQQR ~ RQQP ~ QPRQ ~ QRPQ. We only need to record one.
                    if (rInt < sInt)
                    {
                        if (pInt < qInt)
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, s, r }, coeff = -2.0 * coefficient
                            });
                        }
                        else
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { q, p, s, r }, coeff = 2.0 * coefficient
                            });
                        }
                    }
                    else
                    {
                        if (pInt < qInt)
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, r, s }, coeff = 2.0 * coefficient
                            });
                        }
                        else
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { q, p, r, s }, coeff = -2.0 * coefficient
                            });
                        }
                    }
                }
                else if (qInt == sInt && pInt < rInt && rInt != sInt && pInt != sInt)
                {
                    // PQRQ
                    // For any distinct pqr, [i;j;k;j] generates {p, q, r, q}, {q, r, q, p}, {q, p, q, r}, {r, q, p, q}. We only need to record one.
                    if (pInt < qInt)
                    {
                        if (rInt > qInt)
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, r, s }, coeff = 2.0 * coefficient
                            });
                        }
                        else
                        {
                            AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                                CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, s, r }, coeff = -2.0 * coefficient
                            });
                        }
                    }
                    else
                    {
                        AddFermionTerm(FermionTermType.Common.PQQRTermType, new FermionTerm {
                            CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { q, p, r, s }, coeff = -2.0 * coefficient
                        });
                    }
                }
                else if (pInt < qInt && pInt < rInt && pInt < sInt && qInt != rInt && qInt != sInt && rInt != sInt)
                {
                    // PQRS
                    // For any distinct pqrs, [i;j;k;l] generates
                    // {{p, q, r, s}<->{s, r, q, p}<->{q, p, s, r}<->{r, s, p, q},
                    // {1,2,3,4}<->{4,3,2,1}<->{2,1,4,3}<->{3,4,1,2}
                    // {p, r, q, s}<->{s, q, r, p}<->{r, p, s, q}<->{q, s, p, r}}
                    // 1324, 4231, 3142, 2413
                    if (rInt < sInt)
                    {
                        AddFermionTerm(FermionTermType.Common.PQRSTermType, new FermionTerm {
                            CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, s, r }, coeff = -2.0 * coefficient
                        });
                    }
                    else
                    {
                        AddFermionTerm(FermionTermType.Common.PQRSTermType, new FermionTerm {
                            CreationAnnihilationIndices = new Int64[] { 1, 1, 0, 0 }, SpinOrbitalIndices = new SpinOrbital[] { p, q, r, s }, coeff = 2.0 * coefficient
                        });
                    }
                }
            }
        }