%%%% ECE210-B Matlab Seminar, Homework 2.
% james kit paul thomas ryan murphy, 1/31/24    
% this is james' full name. -A. 
		% <+.01> i needed this.

% this should run in octave (7.3.0)!
		% <3, turns out it also runs in MATLAB. love the comment,
		% though.
close all; clear; clc;

%% Spatial Awareness
% (1)
A = reshape(0:63, 8, 8).';
B = 2 .^ A;

% (2)
B_colvec = reshape(B, [], 1);

% primes(64) = [2, 3, 5... 61]
% since we're asking for the prime indices from index 1 to index 64,
% getting all prime numbers within [0, 64] seems fitting
B_primeinds = B_colvec(primes(length(B_colvec)));
		% very clean, like it.

% (3)
B_primeinds_geometric_mean = prod(B_primeinds) .^ (1 / length(B_primeinds));

% (4)
% (1, :) -- Grab ALL of row 1 (aka the top row) in the matrix.
% fliplr -- flip left to right
A(1, :) = fliplr(A(1, :));	% :)

% (5)
% Keep all rows, keep all cols minus the end col
A_lastcol_del = A(1:8, 1:end-1);
		% that's one way! another is to assign to A (or a copy) with an
		% empty matrix:
		% A(:, end) = [];


%% Smallest Addition
% (1)
N = 100;
t = linspace(0, 6.66, N);

erf_integrand = exp(-t .^ 2);

figure;
title("Plot of e^{-t^2}, (100pts, [0, 6.66])");
xlabel("time");
ylabel("e^{-t^2}");
plot(t, erf_integrand);

% (2)
% derivative approximation,
% form some dt being the distance between points on our time axis
% form the derivative by dividing the formed erf_integrand over dt 
dt = t(2) - t(1);
dfdt = diff(erf_integrand) ./ dt;

% padding op
dfdt = dfdt([1 1:end]);

% mean squared error between approximate taken and real erf deriv
dfdt_anal = -2 .* t .* exp( -t .^ 2);
msqerr_dfdt = (1 / N) .* mean((dfdt - dfdt_anal) .^ 2);
		% <-.03> (repeated) this will underreport -- `mean()` already
		% does a divide by the length of the array, so another divide
		% by N is not necessary.

figure; hold on;
title('d/dt[e^{-t^2}] -- analytic vs approximate (100pts, [0, 6.66])');
xlabel('time');
ylabel('d/dt[-exp(t^2)]');

plot(t, dfdt, 'g-');
plot(t, dfdt_anal, 'r-');

% (3)

% integral approx
% take the area under the curve between t(n) and t(n-1)
% sum the area and scale by 2/sqrt(pi)

figure; hold on;
title('erf -- analytic vs approximate (100pts, [0, 6.66])');
xlabel('time');
ylabel('erf(t)');

% baseline
erf_anal = (sqrt(pi) ./ 2) .* erf(t);

% cum sum
erf_approx_cumsum = cumsum(erf_integrand) .* dt;
msqerr_erf_cumsum = (1 / N) .* mean((erf_approx_cumsum - erf_anal) .^ 2);

% cum trapz
erf_approx_cumtrapz = cumtrapz(erf_integrand) .* dt;
msqerr_erf_cumtrapz = (1 / N) .* mean((erf_approx_cumtrapz - erf_anal) .^ 2);

%{
	The mean-square error of the cumsum op is 5 orders of magnitude larger
		than the result from the cumtrapz op.
	Qualitatively, the cumsum approximation (blue line) is noticably further 
		away than the cumtrapz approximation (red line). the cumsum also has
		a non-negligible y intercept, which is much further than the cumtrapz
		assumption. However, the cumtrapz is basically on top of the analytic
		function (green line), which is especially novel considering both ops
		used the same data set. 
%}
		% <+.02> love the explanatory comment. `cumtrapz` is
		% surprisingly good at what it does!

% check
plot(t, erf_approx_cumsum, "b-");
plot(t, erf_approx_cumtrapz, "r-");
plot(t, erf_anal, "g-");
