/// <summary>
        /// Creates all fermion terms generated by all symmetries of an orbital integral.
        /// </summary>
        /// <param name="nOrbitals">Total number of distinct orbitals.</param>
        /// <param name="orbitalIntegral">Input orbital integral.</param>
        /// <param name="indexConvention">Indexing scheme from spin-orbitals to integers.</param>
        /// <returns>List of fermion terms generated by all symmetries of an orbital integral.</returns>
        public static IEnumerable <(HermitianFermionTerm, double)> ToHermitianFermionTerms(
            this OrbitalIntegral orbitalIntegral,
            int nOrbitals,
            IndexConvention indexConvention = IndexConvention.UpDown)
        {
            var termType = orbitalIntegral.TermType;

            if (termType == TermType.OrbitalIntegral.OneBody)
            {
                return(orbitalIntegral.ToOneBodySpinOrbitalTerms(nOrbitals, indexConvention));
            }
            else if (termType == TermType.OrbitalIntegral.TwoBody)
            {
                return(orbitalIntegral.ToTwoBodySpinOrbitalTerms(nOrbitals, indexConvention));
            }
            else if (termType == TermType.OrbitalIntegral.Identity)
            {
                return(new List <(HermitianFermionTerm, double)>()
                {
                    (new HermitianFermionTerm(), orbitalIntegral.Coefficient)
                });
            }
            else
            {
                throw new System.NotImplementedException();
            }
        }
        /// <summary>
        /// Creates all fermion terms generated by all symmetries of a one-body orbital integral.
        /// </summary>
        /// <param name="nOrbitals">Total number of distinct orbitals.</param>
        /// <param name="orbitalIntegral">Input orbital integral.</param>
        /// <param name="indexConvention">Indexing scheme from spin-orbitals to integers.</param>
        private static IEnumerable <(HermitianFermionTerm, double)> ToOneBodySpinOrbitalTerms(
            this OrbitalIntegral orbitalIntegral,
            int nOrbitals,
            IndexConvention indexConvention)
        {
            // One-electron orbital integral symmetries
            // ij = ji
            var pqSpinOrbitals = orbitalIntegral.EnumerateOrbitalSymmetries().EnumerateSpinOrbitals();

            var coefficient = orbitalIntegral.Coefficient;

            foreach (var pq in pqSpinOrbitals)
            {
                var pInt = pq[0].ToInt(indexConvention, nOrbitals);
                var qInt = pq[1].ToInt(indexConvention, nOrbitals);
                if (pInt == qInt)
                {
                    yield return(new HermitianFermionTerm(new[] { pInt, qInt }.ToLadderSequence()), orbitalIntegral.Coefficient);
                }
                else if (pInt < qInt)
                {
                    yield return(new HermitianFermionTerm(new[] { pInt, qInt }.ToLadderSequence()), 2.0 * orbitalIntegral.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="orbitalIntegral">Sequence of four-body orbital integrals.</param>
        /// <param name="indexConvention">Indexing scheme from spin-orbitals to integers.</param>
        private static IEnumerable <(HermitianFermionTerm, double)> ToTwoBodySpinOrbitalTerms(
            this OrbitalIntegral orbitalIntegral,
            int nOrbitals,
            IndexConvention indexConvention)
        {
            List <(HermitianFermionTerm, double)> fermionTerms = new List <(HermitianFermionTerm, double)>();
            // 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(indexConvention, nOrbitals);
                var qInt = q.ToInt(indexConvention, nOrbitals);
                var rInt = r.ToInt(indexConvention, nOrbitals);
                var sInt = s.ToInt(indexConvention, nOrbitals);

                // 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
                    yield return(new HermitianFermionTerm(new[] { pInt, qInt, rInt, sInt }.ToLadderSequence()), 1.0 * coefficient);
                }
                else if (pInt == rInt && qInt == sInt && pInt < qInt)
                {
                    // iU jU iU jU | iD jD iD jD
                    // PQPQ
                    yield return(new HermitianFermionTerm(new[] { pInt, qInt, sInt, rInt }.ToLadderSequence()), -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)
                        {
                            yield return(new HermitianFermionTerm(new[]  { pInt, qInt, sInt, rInt }.ToLadderSequence()), -2.0 * coefficient);
                        }
                        else
                        {
                            yield return(new HermitianFermionTerm(new[]  { qInt, pInt, sInt, rInt }.ToLadderSequence()), 2.0 * coefficient);
                        }
                    }
                    else
                    {
                        if (pInt < qInt)
                        {
                            yield return(new HermitianFermionTerm(new[]  { pInt, qInt, rInt, sInt }.ToLadderSequence()), 2.0 * coefficient);
                        }
                        else
                        {
                            yield return(new HermitianFermionTerm(new[]  { qInt, pInt, rInt, sInt }.ToLadderSequence()), -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)
                        {
                            yield return(new HermitianFermionTerm(new[]  { pInt, qInt, rInt, sInt }.ToLadderSequence()), 2.0 * coefficient);
                        }
                        else
                        {
                            yield return(new HermitianFermionTerm(new[]  { pInt, qInt, sInt, rInt }.ToLadderSequence()), -2.0 * coefficient);
                        }
                    }
                    else
                    {
                        yield return(new HermitianFermionTerm(new[]  { qInt, pInt, rInt, sInt }.ToLadderSequence()), -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)
                    {
                        yield return(new HermitianFermionTerm(new[]  { pInt, qInt, sInt, rInt }.ToLadderSequence()), -2.0 * coefficient);
                    }
                    else
                    {
                        yield return(new HermitianFermionTerm(new[]  { pInt, qInt, rInt, sInt }.ToLadderSequence()), 2.0 * coefficient);
                    }
                }
            }
        }