import sys
import csv
import numpy as np

"""
  Note: Standard python has only 64bit double precision for floating point type.
        Numerical python module (numpy) can handle different precisions for ndarray type
    np.float16  # half precision
    np.float32  # single precision
    np.float64  # double precision
    np.float128 # quadruple precision (may not be supported)
"""

outfile = 'sum_error.csv'
h = 0.01
n = 101
iprintstep = 10

def getintarg(iarg, defval=None):
    try:
        return int(argv[iarg])
    except:
        return defval

def getfloatarg(iarg, defval=None):
    try:
        return float(argv[iarg])
    except:
        return defval

argv = sys.argv
if len(argv) >= 2:
    h = getfloatarg(1, h)
if len(argv) >= 3:
    n = getintarg(2, n)
if len(argv) >= 4:
    iprintstep = getintarg(3, iprintstep)

# precision types stored as first elements of ndarrays
h16    = np.array([h],   dtype=np.float16)
sum16  = np.array([0.0], dtype=np.float16)
h32    = np.array([h],   dtype=np.float32)
sum32  = np.array([0.0], dtype=np.float32)
h64    = np.array([h],   dtype=np.float64)
sum64  = np.array([0.0], dtype=np.float64)
#h128   = np.array([h],   dtype=np.float128)
#sum128 = np.array([0.0], dtype=np.float128)

ex     = np.array([0.0], dtype=np.float64)
err16  = np.array([0.0], dtype=np.float64)
err32  = np.array([0.0], dtype=np.float64)
err64  = np.array([0.0], dtype=np.float64)

print()
print("Summing up {} for {} times with different precision floating point types".format(h, n))
print("Write to [{}]".format(outfile))

f = open(outfile, 'w')
fout = csv.writer(f, lineterminator='\n')
fout.writerow(['exact', 'float16', 'float32', 'float64',
               'error(float16)', 'error(float32)', 'error(float64'])

print()
print(f"{'exact':^5}:\t{'sum16 (error)':^28}\t{'sum32 (error)':^28}\t{'sum64 (error)':^28}")

for i in range(n):
    ex[0]    = (i + 1) * h

    sum16[0] += h16[0]
    err16[0] = ex[0] - sum16[0]

    sum32[0] += h32[0]
    err32[0] = ex[0] - sum32[0]

    sum64[0] += h64[0]
    err64[0] = ex[0] - sum64[0]

    fout.writerow([ex[0], sum16[0], sum32[0], sum64[0],
                   err16[0], err32[0], err64[0]])

    if i % iprintstep == 0:
        print(f"{ex[0]:<0.4f}: ", end='')
        print(f"{sum16[0]:<0.18f} ({err16[0]:<+9.2e})  ", end='')
        print(f"{sum32[0]:<0.18f} ({err32[0]:<+9.2e})  ", end='')
        print(f"{sum64[0]:<0.18f} ({err64[0]:<+9.2e})  ", end='')
        print()

f.close()
print()
