/* wavefunction.cc -- compute atomic wavefunctions.  -*- C++ -*-
   Copyright (C) 2024, 2025 Gerardo Ballabio

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include <cmath>
#include <sstream>
#include "wavefunction.h"

static const double pi = 3.141592653589793238;
const double wavefunction::a0 = 0.5291777721092; // Angstroms
const int wavefunction::nmax = 9, wavefunction::lmax = 6;

// radial part of wavefunctions

double wavefunction::r_10(double zr_a0)
{
  const double norm = 2.0;
  return norm * std::exp(-zr_a0);
}

double wavefunction::r_20(double zr_a0)
{
  const double norm = 1.0 / std::sqrt(2.0);
  return norm * (1.0 - zr_a0 *
		 (1.0 / 2.0)) *
    std::exp(-zr_a0 / 2.0);
}

double wavefunction::r_21(double zr_a0)
{
  const double norm = 1.0 / (2.0 * std::sqrt(6.0));
  return norm * zr_a0 * std::exp(-zr_a0 / 2.0);
}

double wavefunction::r_30(double zr_a0)
{
  const double norm = 2.0 / (3.0 * std::sqrt(3.0));
  return norm * (1.0 - zr_a0 *
		 ((2.0 / 3.0) - zr_a0 *
		  (2.0 / 27.0))) *
    std::exp(-zr_a0 / 3.0);
}

double wavefunction::r_31(double zr_a0)
{
  const double norm = 8.0 / (27.0 * std::sqrt(6.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 (1.0 / 6.0)) *
    std::exp(-zr_a0 / 3.0);
}

double wavefunction::r_32(double zr_a0)
{
  const double norm = 4.0 / (81.0 * std::sqrt(30.0));
  return norm * zr_a0*zr_a0 * std::exp(-zr_a0 / 3.0);
}

double wavefunction::r_40(double zr_a0)
{
  const double norm = 1.0 / 4.0;
  return norm * (1.0 - zr_a0 *
		 ((3.0 / 4.0) - zr_a0 *
		  ((1.0 / 8.0) - zr_a0 *
		   (1.0 / 192.0)))) *
    std::exp(-zr_a0 / 4.0);
}

double wavefunction::r_41(double zr_a0)
{
  const double norm = 5.0 / (16.0 * std::sqrt(15.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((1.0 / 4.0) - zr_a0 *
			  (1.0 / 80.0))) *
    std::exp(-zr_a0 / 4.0);
}

double wavefunction::r_42(double zr_a0)
{
  const double norm = 1.0 / (64.0 * std::sqrt(5.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       (1.0 / 12.0)) *
    std::exp(-zr_a0 / 4.0);
}

double wavefunction::r_43(double zr_a0)
{
  const double norm = 1.0 / (768.0 * std::sqrt(35.0));
  return norm * zr_a0*zr_a0*zr_a0 * std::exp(-zr_a0 / 4.0);
}

double wavefunction::r_50(double zr_a0)
{
  const double norm = 2.0 / (5.0 * std::sqrt(5.0));
  return norm * (1.0 - zr_a0 *
		 ((4.0 / 5.0) - zr_a0 *
		  ((4.0 / 25.0) - zr_a0 *
		   ((4.0 / 375.0) - zr_a0 *
		    (2.0 / 9375.0))))) *
    std::exp(-zr_a0 / 5.0);
}

double wavefunction::r_51(double zr_a0)
{
  const double norm = 8.0 / (25.0 * std::sqrt(30.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((3.0 / 10.0) - zr_a0 *
			  ((3.0 / 125.0) - zr_a0 *
			   (1.0 / 1875.0)))) *
    std::exp(-zr_a0 / 5.0);
}

double wavefunction::r_52(double zr_a0)
{
  const double norm = 28.0 / (625.0 * std::sqrt(70.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       ((2.0 / 15.0) - zr_a0 *
				(2.0 / 525.0))) *
    std::exp(-zr_a0 / 5.0);
}

double wavefunction::r_53(double zr_a0)
{
  const double norm = 16.0 / (9375.0 * std::sqrt(70.0));
  return norm * zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
				     (1.0 / 20.0)) *
    std::exp(-zr_a0 / 5.0);
}

double wavefunction::r_54(double zr_a0)
{
  const double norm = 4.0 / (140625.0 * std::sqrt(70.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0 * std::exp(-zr_a0 / 5.0);
}

double wavefunction::r_60(double zr_a0)
{
  const double norm = 1.0 / (3.0 * std::sqrt(6.0));
  return norm * (1.0 - zr_a0 *
		 ((5.0 / 6.0) - zr_a0 *
		  ((5.0 / 27.0) - zr_a0 *
		   ((5.0 / 324.0) - zr_a0 *
		    ((1.0 / 1944.0) - zr_a0 *
		     (1.0 / 174960.0)))))) *
    std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_61(double zr_a0)
{
  const double norm = 35.0 / (54.0 * std::sqrt(210.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((1.0 / 3.0) - zr_a0 *
			  ((1.0 / 30.0) - zr_a0 *
			   ((1.0 / 810.0) - zr_a0 *
			    (1.0 / 68040.0))))) *
    std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_62(double zr_a0)
{
  const double norm = 7.0 / (162.0 * std::sqrt(105.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       ((1.0 / 6.0) - zr_a0 *
				((1.0 / 126.0) - zr_a0 *
				 (1.0 / 9072.0)))) *
    std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_63(double zr_a0)
{
  const double norm = 1.0 / (972.0 * std::sqrt(35.0));
  return norm * zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
				     ((1.0 / 12.0) - zr_a0 *
				      (1.0 / 648.0))) *
    std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_64(double zr_a0)
{
  const double norm = 1.0 / (104976.0 * std::sqrt(7.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
					   (1.0 / 30.0)) *
    std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_65(double zr_a0)
{
  const double norm = 1.0 / (3149280.0 * std::sqrt(77.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * std::exp(-zr_a0 / 6.0);
}

double wavefunction::r_70(double zr_a0)
{
  const double norm = 2.0 / (7.0 * std::sqrt(7.0));
  return norm * (1.0 - zr_a0 *
		 ((6.0 / 7.0) - zr_a0 *
		  ((10.0 / 49.0) - zr_a0 *
		   ((20.0 / 1029.0) - zr_a0 *
		    ((2.0 / 2401.0) - zr_a0 *
		     ((4.0 / 252105.0) - zr_a0 *
		      (4.0 / 37059435.0))))))) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_71(double zr_a0)
{
  const double norm = 8.0 / (49.0 * std::sqrt(21.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((5.0 / 14.0) - zr_a0 *
			  ((2.0 / 49.0) - zr_a0 *
			   ((2.0 / 1029.0) - zr_a0 *
			    ((2.0 / 50421.0) - zr_a0 *
			     (1.0 / 3529470.0)))))) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_72(double zr_a0)
{
  const double norm = 12.0 / (343.0 * std::sqrt(105.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       ((4.0 / 21.0) - zr_a0 *
				((4.0 / 343.0) - zr_a0 *
				 ((2.0 / 7203.0) - zr_a0 *
				  (1.0 / 453789.0))))) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_73(double zr_a0)
{
  const double norm = 16.0 / (16807.0 * std::sqrt(42.0));
  return norm * zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
				     ((3.0 / 28.0) - zr_a0 *
				      ((1.0 / 294.0) - zr_a0 *
				       (1.0 / 30870.0)))) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_74(double zr_a0)
{
  const double norm = 44.0 / (1058841.0 * std::sqrt(154.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
					   ((2.0 / 35.0) - zr_a0 *
					    (2.0 / 2695.0))) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_75(double zr_a0)
{
  const double norm = 8.0 / (12353145.0 * std::sqrt(231.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
						 (1.0 / 42.0)) *
    std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_76(double zr_a0)
{
  const double norm = 4.0 / (259416045.0 * std::sqrt(3003.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * std::exp(-zr_a0 / 7.0);
}

double wavefunction::r_80(double zr_a0)
{
  const double norm = 1.0 / (8.0 * std::sqrt(2.0));
  return norm * (1.0 - zr_a0 *
		 ((7.0 / 8.0) - zr_a0 *
		  ((7.0 / 32.0) - zr_a0 *
		   ((35.0 / 1536.0) - zr_a0 *
		    ((7.0 / 6144.0) - zr_a0 *
		     ((7.0 / 245760.0) - zr_a0 *
		      ((1.0 / 2949120.0) - zr_a0 *
		       (1.0 / 660602880.0)))))))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_81(double zr_a0)
{
  const double norm = 7.0 / (64.0 * std::sqrt(14.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((3.0 / 8.0) - zr_a0 *
			  ((3.0 / 64.0) - zr_a0 *
			   ((1.0 / 384.0) - zr_a0 *
			    ((1.0 / 14336.0) - zr_a0 *
			     ((1.0 / 1146880.0) - zr_a0 *
			      (1.0 / 247726080.0))))))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_82(double zr_a0)
{
  const double norm = 21.0 / (512.0 * std::sqrt(210.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       ((5.0 / 24.0) - zr_a0 *
				((5.0 / 336.0) - zr_a0 *
				 ((5.0 / 10752.0) - zr_a0 *
				  ((5.0 / 774144.0) - zr_a0 *
				   (1.0 / 30965760.0)))))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_83(double zr_a0)
{
  const double norm = 11.0 / (4096.0 * std::sqrt(462.0));
  return norm * zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
				     ((1.0 / 8.0) - zr_a0 *
				      ((1.0 / 192.0) - zr_a0 *
				       ((1.0 / 11520.0) - zr_a0 *
					(1.0 / 2027520.0))))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_84(double zr_a0)
{
  const double norm = 11.0 / (294912.0 * std::sqrt(154.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
					   ((3.0 / 40.0) - zr_a0 *
					    ((3.0 / 1760.0) - zr_a0 *
					     (1.0 / 84480.0)))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_85(double zr_a0)
{
  const double norm = 13.0 / (3932160.0 * std::sqrt(6006.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
						 ((1.0 / 24.0) - zr_a0 *
						  (1.0 / 2496.0))) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_86(double zr_a0)
{
  const double norm = 1.0 / (94371840.0 * std::sqrt(858.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
						       (1.0 / 56.0)) *
    std::exp(-zr_a0 / 8.0);
}

double wavefunction::r_90(double zr_a0)
{
  const double norm = 2.0 / 27.0;
  return norm * (1.0 - zr_a0 *
		 ((8.0 / 9.0) - zr_a0 *
		  ((56.0 / 243.0) - zr_a0 *
		   ((56.0 / 2187.0) - zr_a0 *
		    ((28.0 / 19683.0) - zr_a0 *
		     ((112.0 / 2657205.0) - zr_a0 *
		      ((16.0 / 23914845.0) - zr_a0 *
		       ((8.0 / 1506635235.0) - zr_a0 *
			(2.0 / 122037454035.0))))))))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_91(double zr_a0)
{
  const double norm = 40.0 / (729.0 * std::sqrt(5.0));
  return norm * zr_a0 * (1.0 - zr_a0 *
			 ((7.0 / 18.0) - zr_a0 *
			  ((7.0 / 135.0) - zr_a0 *
			   ((7.0 / 2187.0) - zr_a0 *
			    ((2.0 / 19683.0) - zr_a0 *
			     ((1.0 / 590490.0) - zr_a0 *
			      ((1.0 / 71744535.0) - zr_a0 *
			       (1.0 / 22599528525.0)))))))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_92(double zr_a0)
{
  const double norm = 308.0 / (6561.0 * std::sqrt(385.0));
  return norm * zr_a0*zr_a0 * (1.0 - zr_a0 *
			       ((2.0 / 9.0) - zr_a0 *
				((10.0 / 567.0) - zr_a0 *
				 ((10.0 / 15309.0) - zr_a0 *
				  ((5.0 / 413343.0) - zr_a0 *
				   ((2.0 / 18600435.0) - zr_a0 *
				    (2.0 / 5524329195.0))))))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_93(double zr_a0)
{
  const double norm = 176.0 / (59049.0 * std::sqrt(770.0));
  return norm * zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
				     ((5.0 / 36.0) - zr_a0 *
				      ((5.0 / 729.0) - zr_a0 *
				       ((1.0 / 6561.0) - zr_a0 *
					((1.0 / 649539.0) - zr_a0 *
					 (1.0 / 175375530.0)))))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_94(double zr_a0)
{
  const double norm = 572.0 / (4782969.0 * std::sqrt(2002.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
					   ((4.0 / 45.0) - zr_a0 *
					    ((4.0 / 1485.0) - zr_a0 *
					     ((4.0 / 120285.0) - zr_a0 *
					      (2.0 / 14073345.0))))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_95(double zr_a0)
{
  const double norm = 104.0 / (215233605.0 * std::sqrt(143.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
						 ((1.0 / 18.0) - zr_a0 *
						  ((1.0 / 1053.0) - zr_a0 *
						   (1.0 / 199017.0)))) *
    std::exp(-zr_a0 / 9.0);
}

double wavefunction::r_96(double zr_a0)
{
  const double norm = 4.0 / (387420489.0 * std::sqrt(715.0));
  return norm * zr_a0*zr_a0*zr_a0*zr_a0*zr_a0*zr_a0 * (1.0 - zr_a0 *
						       ((2.0 / 63.0) - zr_a0 *
							(2.0 / 8505.0))) *
    std::exp(-zr_a0 / 9.0);
}

// angular part of wavefunctions (real spherical harmonics)

double wavefunction::y_00(double x_r, double y_r, double z_r)
{
  const double norm = 1.0 / std::sqrt(4.0 * pi);
  return norm;
}

double wavefunction::y_1m1(double x_r, double y_r, double z_r)
{
  const double norm = std::sqrt(3.0 / (4.0 * pi));
  return norm * y_r;
}

double wavefunction::y_10(double x_r, double y_r, double z_r)
{
  const double norm = std::sqrt(3.0 / (4.0 * pi));
  return norm * z_r;
}

double wavefunction::y_11(double x_r, double y_r, double z_r)
{
  const double norm = std::sqrt(3.0 / (4.0 * pi));
  return norm * x_r;
}

double wavefunction::y_2m2(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 2.0) * std::sqrt(15.0 / pi);
  return norm * x_r * y_r;
}

double wavefunction::y_2m1(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 2.0) * std::sqrt(15.0 / pi);
  return norm * y_r * z_r;
}

double wavefunction::y_20(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(5.0 / pi);
  return norm * (3.0 * z_r*z_r - 1.0);
}

double wavefunction::y_21(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 2.0) * std::sqrt(15.0 / pi);
  return norm * x_r * z_r;
}

double wavefunction::y_22(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(15.0 / pi);
  return norm * (x_r*x_r - y_r*y_r);
}

double wavefunction::y_3m3(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(35.0 / (2.0 * pi));
  return norm * y_r * (3.0 * x_r*x_r - y_r*y_r);
}

double wavefunction::y_3m2(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 2.0) * std::sqrt(105.0 / pi);
  return norm * x_r * y_r * z_r;
}

double wavefunction::y_3m1(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(21.0 / (2.0 * pi));
  return norm * y_r * (5.0 * z_r*z_r - 1.0);
}

double wavefunction::y_30(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(7.0 / pi);
  return norm * z_r * (5.0 * z_r*z_r - 3.0);
}

double wavefunction::y_31(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(21.0 / (2.0 * pi));
  return norm * x_r * (5.0 * z_r*z_r - 1.0);
}

double wavefunction::y_32(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(105.0 / pi);
  return norm * z_r * (x_r*x_r - y_r*y_r);
}

double wavefunction::y_33(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(35.0 / (2.0 * pi));
  return norm * x_r * (x_r*x_r - 3.0 * y_r*y_r);
}

double wavefunction::y_4m4(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(35.0 / pi);
  return norm * x_r * y_r * (x_r*x_r - y_r*y_r);
}

double wavefunction::y_4m3(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(35.0 / (2.0 * pi));
  return norm * y_r * z_r * (3.0 * x_r*x_r - y_r*y_r);
}

double wavefunction::y_4m2(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(5.0 / pi);
  return norm * x_r * y_r * (7.0 * z_r*z_r - 1.0);
}

double wavefunction::y_4m1(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(5.0 / (2.0 * pi));
  return norm * y_r * z_r * (7.0 * z_r*z_r - 3.0);
}

double wavefunction::y_40(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(1.0 / pi);
  return norm * (35.0 * z_r*z_r*z_r*z_r - 30.0 * z_r*z_r + 3.0);
}

double wavefunction::y_41(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(5.0 / (2.0 * pi));
  return norm * x_r * z_r * (7.0 * z_r*z_r - 3.0);
}

double wavefunction::y_42(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 8.0) * std::sqrt(5.0 / pi);
  return norm * (x_r*x_r - y_r*y_r) * (7.0 * z_r*z_r - 1.0);
}

double wavefunction::y_43(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(35.0 / (2.0 * pi));
  return norm * x_r * z_r * (x_r*x_r - 3.0 * y_r*y_r);
}

double wavefunction::y_44(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(35.0 / pi);
  return norm * (x_r*x_r*x_r*x_r - 6.0 * x_r*x_r * y_r*y_r + y_r*y_r*y_r*y_r);
}

double wavefunction::y_5m5(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(77.0 / (2.0 * pi));
  return norm * y_r * (5.0 * x_r*x_r*x_r*x_r - 10.0 * x_r*x_r * y_r*y_r +
		       y_r*y_r*y_r*y_r);
}

double wavefunction::y_5m4(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 4.0) * std::sqrt(385.0 / pi);
  return norm * x_r * y_r * z_r * (x_r*x_r - y_r*y_r);
}

double wavefunction::y_5m3(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(385.0 / (2.0 * pi));
  return norm * y_r * (3.0 * x_r*x_r - y_r*y_r) * (9.0 * z_r*z_r - 1.0);
}

double wavefunction::y_5m2(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 4.0) * std::sqrt(1155.0 / pi);
  return norm * x_r * y_r * z_r * (3.0 * z_r*z_r - 1.0);
}

double wavefunction::y_5m1(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(165.0 / pi);
  return norm * y_r * (21.0 * z_r*z_r*z_r*z_r - 14.0 * z_r*z_r + 1.0);
}

double wavefunction::y_50(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(11.0 / pi);
  return norm * z_r * (63.0 * z_r*z_r*z_r*z_r - 70.0 * z_r*z_r + 15.0);
}

double wavefunction::y_51(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(165.0 / pi);
  return norm * x_r * (21.0 * z_r*z_r*z_r*z_r - 14.0 * z_r*z_r + 1.0);
}

double wavefunction::y_52(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 8.0) * std::sqrt(1155.0 / pi);
  return norm * z_r * (x_r*x_r - y_r*y_r) * (3.0 * z_r*z_r - 1.0);
}

double wavefunction::y_53(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(385.0 / (2.0 * pi));
  return norm * x_r * (x_r*x_r - 3.0 * y_r*y_r) * (9.0 * z_r*z_r - 1.0);
}

double wavefunction::y_54(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(385.0 / pi);
  return norm * z_r * (x_r*x_r*x_r*x_r - 6.0 * x_r*x_r * y_r*y_r +
		       y_r*y_r*y_r*y_r);
}

double wavefunction::y_55(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(77.0 / (2.0 * pi));
  return norm * x_r * (x_r*x_r*x_r*x_r - 10.0 * x_r*x_r * y_r*y_r +
		       5.0 * y_r*y_r*y_r*y_r);
}

double wavefunction::y_6m6(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(3003.0 / (2.0 * pi));
  return norm * x_r * y_r * (3.0 * x_r*x_r*x_r*x_r - 10.0 * x_r*x_r * y_r*y_r
			     + 3.0 * y_r*y_r*y_r*y_r);
}

double wavefunction::y_6m5(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(1001.0 / (2.0 * pi));
  return norm * y_r * z_r * (5.0 * x_r*x_r*x_r*x_r -
			     10.0 * x_r*x_r * y_r*y_r + y_r*y_r*y_r*y_r);
}

double wavefunction::y_6m4(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 8.0) * std::sqrt(91.0 / pi);
  return norm * x_r * y_r * (x_r*x_r - y_r*y_r) * (11.0 * z_r*z_r - 1.0);
}

double wavefunction::y_6m3(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(1365.0 / (2.0 * pi));
  return norm * y_r * z_r * (3.0 * x_r*x_r - y_r*y_r) *
    (11.0 * z_r*z_r - 3.0);
}

double wavefunction::y_6m2(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(1365.0 / (2.0 * pi));
  return norm * x_r * y_r * (33.0 * z_r*z_r*z_r*z_r - 18.0 * z_r*z_r + 1.0);
}

double wavefunction::y_6m1(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(273.0 /  pi);
  return norm * y_r * z_r * (33.0 * z_r*z_r*z_r*z_r - 30.0 * z_r*z_r + 5.0);
}

double wavefunction::y_60(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 32.0) * std::sqrt(13.0 /  pi);
  return norm * (231.0 * z_r*z_r*z_r*z_r*z_r*z_r - 315.0 * z_r*z_r*z_r*z_r +
		 105.0 * z_r*z_r - 5.0);
}

double wavefunction::y_61(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(273.0 /  pi);
  return norm * x_r * z_r * (33.0 * z_r*z_r*z_r*z_r - 30.0 * z_r*z_r + 5.0);
}

double wavefunction::y_62(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 32.0) * std::sqrt(1365.0 / (2.0 * pi));
  return norm * (x_r*x_r - y_r*y_r) * (33.0 * z_r*z_r*z_r*z_r -
				       18.0 * z_r*z_r + 1.0);
}

double wavefunction::y_63(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 16.0) * std::sqrt(1365.0 / (2.0 * pi));
  return norm * x_r * z_r * (x_r*x_r - 3.0 * y_r*y_r) *
    (11.0 * z_r*z_r - 3.0);
}

double wavefunction::y_64(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 32.0) * std::sqrt(91.0 / pi);
  return norm * (x_r*x_r*x_r*x_r - 6.0 * x_r*x_r * y_r*y_r +
		 y_r*y_r*y_r*y_r) * (11.0 * z_r*z_r - 1.0);
}

double wavefunction::y_65(double x_r, double y_r, double z_r)
{
  const double norm = (3.0 / 16.0) * std::sqrt(1001.0 / (2.0 * pi));
  return norm * x_r * z_r * (x_r*x_r*x_r*x_r - 10.0 * x_r*x_r * y_r*y_r +
			     5.0 * y_r*y_r*y_r*y_r);
}

double wavefunction::y_66(double x_r, double y_r, double z_r)
{
  const double norm = (1.0 / 32.0) * std::sqrt(3003.0 / (2.0 * pi));
  return norm * (x_r*x_r*x_r*x_r*x_r*x_r - 15.0 * x_r*x_r*x_r*x_r * y_r*y_r +
		 15.0 * x_r*x_r * y_r*y_r*y_r*y_r - y_r*y_r*y_r*y_r*y_r*y_r);
}

wavefunction::wavefunction(unsigned z_)
{
  if (!set_z(z_))
    set_z(1);
  set(1, 0, 0);
}

bool wavefunction::set_z(unsigned z_)
{
  if (z_ < 1)
    return false;
  z = z_;
  z_a0 = z / a0;
  z_a0_32 = std::sqrt(z_a0*z_a0*z_a0);
  return true;
}

bool wavefunction::set(int n_, int l_, int m_)
{
  // physical constraints (allowed values of quantum numbers)
  if (n_ < 1 || l_ < 0 || l_ >= n_ || m_ < -l_ || m_ > l_)
    return false;
  // implementation limits (functions have been written)
  if (n_ > nmax || l_ > lmax)
    return false;

  if (n_ == n && l_ == l && m_ == m)
    return true;

  // set quantum numbers
  n = n_;
  l = l_;
  m = m_;

  // set radial part
  std::ostringstream os;
  os << n;
  label = os.str();
  switch (n)
    {
    default: // suppress warning
    case 1:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_10; max = 2.0; break;
	}
      break;
    case 2:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_20; max = 0.7071067812; break;
	case 1: rf = r_21; max = 0.1501861530; break;
	}
      break;
    case 3:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_30; max = 0.3849001795;  break;
	case 1: rf = r_31; max = 0.08367435043; break;
	case 2: rf = r_32; max = 0.04392662960; break;
	}
      break;
    case 4:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_40; max = 0.25;          break;
	case 1: rf = r_41; max = 0.05480359957; break;
	case 2: rf = r_42; max = 0.02920348926; break;
	case 3: rf = r_43; max = 0.01893498870; break;
	}
      break;
    case 5:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_50; max = 0.1788854382;   break;
	case 1: rf = r_51; max = 0.03936468739;  break;
	case 2: rf = r_52; max = 0.02115172301;  break;
	case 3: rf = r_53; max = 0.01380323007;  break;
	case 4: rf = r_54; max = 0.009963008272; break;
	}
      break;
    case 6:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_60; max = 0.1360827635;   break;
	case 1: rf = r_61; max = 0.03000769051;  break;
	case 2: rf = r_62; max = 0.01619554883;  break;
	case 3: rf = r_63; max = 0.01064393595;  break;
	case 4: rf = r_64; max = 0.007677679230; break;
	case 5: rf = r_65; max = 0.005924848180; break;
	}
      break;
    case 7:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_70; max = 0.1079898494;   break;
	case 1: rf = r_71; max = 0.02384248527;  break;
	case 2: rf = r_72; max = 0.01290183607;  break;
	case 3: rf = r_73; max = 0.008515391187; break;
	case 4: rf = r_74; max = 0.006177287773; break;
	case 5: rf = r_75; max = 0.004735958393; break;
	case 6: rf = r_76; max = 0.003828373861; break;
	}
      break;
    case 8:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_80; max = 0.08838834765;  break;
	case 1: rf = r_81; max = 0.01953046710;  break;
	case 2: rf = r_82; max = 0.01058619359;  break;
	case 3: rf = r_83; max = 0.007005841131; break;
	case 4: rf = r_84; max = 0.005101819445; break;
	case 5: rf = r_85; max = 0.003928527657; break;
	case 6: rf = r_86; max = 0.003139482274; break;
	}
      break;
    case 9:
      switch (l)
	{
	default: // suppress warning
	case 0: rf = r_90; max = 0.07407407407;  break;
	case 1: rf = r_91; max = 0.01637656155;  break;
	case 2: rf = r_92; max = 0.008886758438; break;
	case 3: rf = r_93; max = 0.005891776791; break;
	case 4: rf = r_94; max = 0.004301612437; break;
	case 5: rf = r_95; max = 0.003323598593; break;
	case 6: rf = r_96; max = 0.002664501335; break;
	}
      break;
    }

  // set angular part
  switch (l)
    {
    default: // suppress warning
    case 0:
      switch (m)
	{
	default: // suppress warning
	case 0: yf = y_00; max *= 0.2820947918; label += "s"; break;
	}
      break;
    case 1:
      switch (m)
	{
	default: // suppress warning
	case -1: yf = y_1m1; max *= 0.4886025119;
	  label += "p<sub>y</sub>"; break;
	case 0:  yf = y_10;  max *= 0.4886025119;
	  label += "p<sub>z</sub>"; break;
	case 1:  yf = y_11;  max *= 0.4886025119;
	  label += "p<sub>x</sub>"; break;
	}
      break;
    case 2:
      switch (m)
	{
	default: // suppress warning
	case -2: yf = y_2m2; max *= 0.5462742153;
	  label += "d<sub>xy</sub>";                          break;
	case -1: yf = y_2m1; max *= 0.5462742153;
	  label += "d<sub>yz</sub>";                          break;
	case 0:  yf = y_20;  max *= 0.6307831305;
	  label += "d<sub>z<sup>2</sup></sub>";               break;
	case 1:  yf = y_21;  max *= 0.5462742153;
	  label += "d<sub>xz</sub>";                          break;
	case 2:  yf = y_22;  max *= 0.5462742153;
	  label += "d<sub>x<sup>2</sup>-y<sup>2</sup></sub>"; break;
	}
      break;
    case 3:
      switch (m)
	{
	default: // suppress warning
	case -3: yf = y_3m3; max *= 0.5900435899;
	  label += "f<sub>y(3x<sup>2</sup>-y<sup>2</sup>)</sub>"; break;
	case -2: yf = y_3m2; max *= 0.5562984315;
	  label += "f<sub>xyz</sub>";                             break;
	case -1: yf = y_3m1; max *= 0.6293798293;
	  label += "f<sub>yz<sup>2</sup></sub>";                  break;
	case 0:  yf = y_30;  max *= 0.7463526652;
	  label += "f<sub>z<sup>3</sup></sub>";                   break;
	case 1:  yf = y_31;  max *= 0.6293798293;
	  label += "f<sub>xz<sup>2</sup></sub>";                  break;
	case 2:  yf = y_32;  max *= 0.5562984315;
	  label += "f<sub>z(x<sup>2</sup>-y<sup>2</sup>)</sub>";  break;
	case 3:  yf = y_33;  max *= 0.5900435899;
	  label += "f<sub>x(x<sup>2</sup>-3y<sup>2</sup>)</sub>"; break;
	}
      break;
    case 4:
      switch (m)
	{
	default: // suppress warning
	case -4: yf = y_4m4; max *= 0.6258357354;
	  label += "g<sub>xy(x<sup>2</sup>-y<sup>2</sup>)</sub>"; break;
	case -3: yf = y_4m3; max *= 0.5748668305;
	  label += "g<sub>zy<sup>3</sup></sub>";                  break;
	case -2: yf = y_4m2; max *= 0.6082551616;
	  label += "g<sub>z<sup>2</sup>xy</sub>";                 break;
	case -1: yf = y_4m1; max *= 0.7065306946;
	  label += "g<sub>z<sup>3</sup>y</sub>";                  break;
	case 0:  yf = y_40;  max *= 0.8462843753;
	  label += "g<sub>z<sup>4</sup></sub>";                   break;
	case 1:  yf = y_41;  max *= 0.7065306946;
	  label += "g<sub>z<sup>3</sup>x</sub>";                  break;
	case 2:  yf = y_42;  max *= 0.6082551616;
	  label += "g<sub>z<sup>2</sup>(x<sup>2</sup>-y<sup>2</sup>)</sub>";
	  break;
	case 3:  yf = y_43;  max *= 0.5748668305;
	  label += "g<sub>zx<sup>3</sup></sub>";                  break;
	case 4:  yf = y_44;  max *= 0.6258357354;
	  label += "g<sub>x<sup>4</sup>+y<sup>4</sup></sub>";     break;
	}
      break;
    case 5:
      switch (m)
	{
	default: // suppress warning
	case -5: yf = y_5m5; max *= 0.6563820568;
	  label += "h<sub>yx<sup>4</sup></sub>";                     break;
	case -4: yf = y_5m4; max *= 0.5940892204;
	  label += "h<sub>z(4x<sup>3</sup>y-4xy<sup>3</sup>)</sub>"; break;
	case -3: yf = y_5m3; max *= 0.6097734601;
	  label += "h<sub>z<sup>2</sup>y<sup>3</sup></sub>";         break;
	case -2: yf = y_5m2; max *= 0.6619399678;
	  label += "h<sub>z<sup>3</sup>xy</sub>";                    break;
	case -1: yf = y_5m1; max *= 0.7772865041;
	  label += "h<sub>yz<sup>4</sup></sub>";                     break;
	case 0:  yf = y_50;  max *= 0.9356025796;
	  label += "h<sub>z<sup>5</sup></sub>";                      break;
	case 1:  yf = y_51;  max *= 0.7772865041;
	  label += "h<sub>xz<sup>4</sup></sub>";                     break;
	case 2:  yf = y_52;  max *= 0.6619399678;
	  label += "h<sub>z<sup>3</sup>(x<sup>2</sup>-y<sup>2</sup>)</sub>";
	  break;
	case 3:  yf = y_53;  max *= 0.6097734601;
	  label += "h<sub>z<sup>2</sup>x<sup>3</sup></sub>";         break;
	case 4:  yf = y_54;  max *= 0.5940892204;
	  label += "h<sub>z(x<sup>4</sup>-6x<sup>2</sup>y<sup>2</sup>+"
	    "y<sup>4</sup>)</sub>";                                  break;
	case 5:  yf = y_55;  max *= 0.6563820568;
	  label += "h<sub>xy<sup>4</sup></sub>";                     break;
	}
      break;
    case 6:
      switch (m)
	{
	default: // suppress warning
	case -6: yf = y_6m6; max *= 0.6831841052;
	  label += "i<sub>6yx<sup>5</sup>-20x<sup>2</sup>y<sup>3</sup>+"
	    "6xy<sup>5</sup></sub>";                               break;
	case -5: yf = y_6m5; max *= 0.6124908939;
	  label += "i<sub>yz(5x<sup>4</sup>-10x<sup>2</sup>y<sup>2</sup>+"
	    "y<sup>4</sup>)</sub>";                                break;
	case -4: yf = y_6m4; max *= 0.6177715344;
	  label += "i<sub>4xy(x<sup>2</sup>-y<sup>2</sup>)(11z<sup>2</sup>-"
	    "r<sup>2</sup>)</sub>";                                break;
	case -3: yf = y_6m3; max *= 0.6501794433;
	  label += "i<sub>y(3x<sup>2</sup>-y<sup>2</sup>)(11z<sup>3</sup>-"
	    "3zr<sup>2</sup>)</sub>";                              break;
	case -2: yf = y_6m2; max *= 0.7135675843;
	  label += "i<sub>xyz<sup>4</sup></sub>";                  break;
	case -1: yf = y_6m1; max *= 0.8426690685;
	  label += "i<sub>yz<sup>5</sup></sub>";                   break;
	case 0:  yf = y_60;  max *= 1.0171072363;
	  label += "i<sub>z<sup>6</sup></sub>";                    break;
	case 1:  yf = y_61;  max *= 0.8426690685;
	  label += "i<sub>xz<sup>5</sup></sub>";                   break;
	case 2:  yf = y_62;  max *= 0.7135675843;
	  label += "i<sub>(x<sup>2</sup>-y<sup>2</sup>)z<sup>4</sup></sub>";
	  break;
	case 3:  yf = y_63;  max *= 0.6501794433;
	  label += "i<sub>x(x<sup>2</sup>-3y<sup>2</sup>)(11z<sup>3</sup>-"
	    "3zr<sup>2</sup>)</sub>";                              break;
	case 4:  yf = y_64;  max *= 0.6177715344;
	  label += "i<sub>(x<sup>4</sup>-6x<sup>2</sup>y<sup>2</sup>+"
	    "y<sup>4</sup>)(11z<sup>2</sup>-r<sup>2</sup>)</sub>"; break;
	case 5:  yf = y_65;  max *= 0.6124908939;
	  label += "i<sub>xz(x<sup>4</sup>-10x<sup>2</sup>y<sup>2</sup>+"
	    "5y<sup>4</sup>)</sub>";                               break;
	case 6:  yf = y_66;  max *= 0.6831841052;
	  label += "i<sub>x<sup>6</sup>-15x<sup>4</sup>y<sup>2</sup>+"
	    "15x<sup>2</sup>y<sup>4</sup>-y<sup>6</sup></sub>";    break;
	}
      break;
    }

  return true;
}

const double wavefunction::default_scale()
{
  // empirical formula, works well enough
  return 1.2 * n * (n + 1.5) / z;
}

const double wavefunction::default_vscale()
{
  // another empirical formula
  return 1.0 + 0.25 * n / (l + 0.25);
}

const double wavefunction::max_wave()
{
  return z_a0_32 * max;
}

const double wavefunction::radial(double r)
{
  // z_a0_32 is factored out of rf because it has to be a static function
  return z_a0_32 * rf(z_a0 * r);
}

const double wavefunction::angular(double x_r, double y_r, double z_r)
{
  return yf(x_r, y_r, z_r);
}

const double wavefunction::wave(double x, double y, double z)
{
  double r = std::sqrt(x*x + y*y + z*z) + 1e-100; // ensure r is nonzero
  return radial(r) * angular(x / r, y / r, z / r);
}

const double wavefunction::density(double x, double y, double z)
{
  double w = wave(x, y, z);
  return w*w;
}
