"""
file: 
    dnexample.py
description:
    an example of forward mode automatic differentiation using dual numbers
url:
    https://kyscg.github.io/2025/05/18/autodiffpython
author:
    kyscg
"""

class DN:
    def __init__(self, real, dual):
        self.real = real
        self.dual = dual

    def __add__(self, other):
        if (isinstance(other, DN)):
            real = self.real + other.real
            dual = self.dual + other.dual
            return DN(real, dual)
        else:
            real = self.real + other
            dual = self.dual
            return DN(real, dual)
    __radd__ = __add__

    def __mul__(self, other):
        if (isinstance(other, DN)):
            real = self.real * other.real
            dual = self.real * other.dual + self.dual * other.real
            return DN(real, dual)
        else:
            real = self.real * other
            dual = self.dual * other
            return DN(real, dual)
    __rmul__ = __mul__


a = DN(3, 0)
b = a + 3
c = 4 + a

d = DN(4, 2)
e = d * 3
f = DN(6, 2)
g = d * f


print("a:", a.real, a.dual)
print("b:", b.real, b.dual)
print("c:", c.real, c.dual)

print("d:", d.real, d.dual)
print("e:", e.real, e.dual)
print("f:", f.real, f.dual)
print("g:", g.real, g.dual)
print('---')


def f(x):
    return 7 * x * x + 3 * x + 4

def diff(f, x):
    return f(DN(x, 1)).dual

print('f(1):', f(1))
print('df(1):', diff(f, 1))