#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 13 14:59:32 2024

@author: sam
"""

import numpy as np

roots = []
minimum = []

'''functions'''
def f(x):
    return x - np.cos(x)

def bisection(f,a,b,tol):
    while abs(f(b) - f(a)) >= tol:
        c = (a+b)/2
        if f(a)*f(c) > 0:
            a = c
            #print('took right side')
        elif f(c)*f(b) > 0:
            b = c
            #print('took left side')
        else:
            print('Fail')
            break
    return (a+b)/2

def bracket(f,a,b):
    for i in range(-2000,2000):
        d = i/1000
        e = (i+1)/1000
        if f(d)*f(e) < 0: #check for change in sign
            roots.append([d,e]) #add bracketed roots to list
        else:
            continue

def bracketrevised(f,a,b,N):
    x = np.linspace(a,b,N)
    for i in range(len(x)-1):
        if f(x[i])*f(x[i+1])<0:
            roots.append([x[i],x[i+1]])

def secant(f,a,b,tol):
    n = 0
    while abs(a-b) >= tol and n < 100:
        c = (b*f(a)-a*f(b))/(f(a)-f(b))
        b = a
        a = c
        n += 1
    return c

def falseposition(f,a,b,tol):
    while abs(b-a) >= tol:
        c = (b*f(a)-a*f(b))/(f(a)-f(b))
      
        if f(a)*f(c)<0:    #check if n,p bracket the root
            b = a
            a = c
        elif f(b)*f(c)<0:  #check if m,p bracket the root
            b = a
            a = c
    return c

def minbracket(f,a,b,N):
    h = (b-a)/N
    for i in range(1,int(N)):
        x = a+h*i
        if f(x)<f(x-h) and f(x)<f(x+h):
            minimum.append([x-h,x+h])
        else:
            continue
    
def goldensearch(f,a,b,tol):
    j = 0
    for i in minimum:
        a = i[0]
        b = i[1]
        while abs(b-a) > tol:
            Φ = (1+np.sqrt(5))/2
            c = b - (b-a)/Φ
            d = a + (b-a)/Φ
            if f(d)<f(c):
                a = c
                c = d
            else:
                b = d
        minimum[j] = [(a+b)/2]
        j += 1
    return c

def findallzeros(f,a,b,tol):
    x = np.linspace(a,b,10000)
    for i in range(len(x)-1):
        if f(x[i])*f(x[i+1])<0:
            roots.append([x[i],x[i+1]])
    for i in range(len(roots)):
        [c,d] = roots[i][:]
        while abs(c-d) >= tol:
            e = (d*f(c)-c*f(d))/(f(c)-f(d))
            d = c
            c = e
        roots[i] = e
    return roots



#run 'mysearch' procedure
a = 0
b = 1
N = 1e5
tol = 0.0001
x = bisection(f,a,b,tol)
print('Zero found at',x)
minbracket(f,a,b,N)
goldensearch(f,a,b,tol)
print('(golden search) Minimum(s) found at ...')
for i in range(len(minimum)):
    print(minimum[i])