From e7cb1fb86a10dcd9494ac194c75f11db73f5d266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C3=96zkan?= Date: Thu, 2 Oct 2025 14:01:25 -0400 Subject: [PATCH 1/2] divided +gen --- +datop/apply_func_along_dimension.m | 44 ++++ +datop/empty_like.m | 6 + +datop/extract_numbers_from_string.m | 25 ++ +datop/hstack_struct.m | 66 +++++ +datop/make_column.m | 5 + +datop/make_row.m | 5 + +datop/struct_to_varargin.m | 22 ++ +num/absargmin.m | 10 + +num/find_closest_integer_divisor_pair.m | 12 + +num/ifbetween.m | 4 + +num/ifinrange.m | 5 + +num/ifwithin.m | 5 + +stats/range.m | 15 ++ +stats/robust_z.m | 63 +++++ +util/Timer.m | 297 +++++++++++++++++++++++ 15 files changed, 584 insertions(+) create mode 100644 +datop/apply_func_along_dimension.m create mode 100644 +datop/empty_like.m create mode 100644 +datop/extract_numbers_from_string.m create mode 100644 +datop/hstack_struct.m create mode 100644 +datop/make_column.m create mode 100644 +datop/make_row.m create mode 100644 +datop/struct_to_varargin.m create mode 100644 +num/absargmin.m create mode 100644 +num/find_closest_integer_divisor_pair.m create mode 100644 +num/ifbetween.m create mode 100644 +num/ifinrange.m create mode 100644 +num/ifwithin.m create mode 100644 +stats/range.m create mode 100644 +stats/robust_z.m create mode 100644 +util/Timer.m diff --git a/+datop/apply_func_along_dimension.m b/+datop/apply_func_along_dimension.m new file mode 100644 index 0000000..155a5a4 --- /dev/null +++ b/+datop/apply_func_along_dimension.m @@ -0,0 +1,44 @@ +function varargout = apply_func_along_dimension(arr, iDim, func, varargin) +dim_size = size(arr); +num_dims = ndims(arr); + +% --- Input Validation --- +if iDim > num_dims || iDim <= 0 || ~isnumeric(iDim) || fix(iDim) ~= iDim + error('iDim must be a positive integer less than or equal to the number of dimensions of arr.'); +end +if ~isa(func, 'function_handle') + error('func must be a function handle.'); +end + +% --- Determine Output Size (and preallocate) --- +% Apply the function to the *first* slice to determine the output size. +first_slice_indices = repmat({1}, 1, num_dims); +first_slice_indices{iDim} = ':'; +first_slice = squeeze(arr(first_slice_indices{:})); +outputN = cell([1,nargout]); +[outputN{:}] = func(first_slice, varargin{:}); +output_size_iDim = cellfun(@(x) numel(x), outputN); +% Calculate the size of the output array +op_size = repmat(dim_size,nargout,1); +op_size(:,iDim) = output_size_iDim; +% Preallocate the output array. +varargout = arrayfun(@(i) zeros(op_size(i,:), 'like', outputN{i}), 1:nargout, 'UniformOutput',false); + +% --- Prepare for Looping --- +% Create a cell array to index all dimensions *except* iDim. +slice_idx = arrayfun(@(x) 1:x, dim_size, 'UniformOutput',false); +slice_idx{iDim} = ':'; +slice_idx = table2cell(combinations(slice_idx{:})); + +for iSlice = 1:size(slice_idx,1) + + [outputN{:}] = func(squeeze(arr(slice_idx{iSlice,:})), varargin{:}); + + for iOp = 1:nargout + + varargout{iOp}(slice_idx{iSlice,:}) = outputN{iOp}; + + end +end + +end \ No newline at end of file diff --git a/+datop/empty_like.m b/+datop/empty_like.m new file mode 100644 index 0000000..101337f --- /dev/null +++ b/+datop/empty_like.m @@ -0,0 +1,6 @@ +function emp = empty_like(x) +% Creates an empty instance of the class of x + +emp = feval(sprintf("%s.empty",class(x))); + +end \ No newline at end of file diff --git a/+datop/extract_numbers_from_string.m b/+datop/extract_numbers_from_string.m new file mode 100644 index 0000000..0122698 --- /dev/null +++ b/+datop/extract_numbers_from_string.m @@ -0,0 +1,25 @@ +function varargout = extract_numbers_from_string(x) +% Extracts all sequences of digits from a string. +% numericVars = extract_numbers_from_string(x) finds all sequences of one or more +% digits in the input string x and returns them as a numeric array. +% +% [numericVars, isNumeric] = extract_numbers_from_string(x) also returns a logical +% array 'isNumeric' the same size as x, where true indicates that the +% corresponding character in x is a digit. +% +% Args: +% x: The input string. +% +% Returns: +% numericVars: A numeric array containing the numbers found in the string. +% Returns an empty array if no numbers are found. +% isNumeric: (Optional) A logical array indicating the position of digits. + +varargout = cell(1, nargout); + +varargout{1} = str2double(regexp(x, '\d+', 'match')); + +if nargout > 1 + varargout{2} = isstrprop(x,'digit'); +end +end \ No newline at end of file diff --git a/+datop/hstack_struct.m b/+datop/hstack_struct.m new file mode 100644 index 0000000..2fe072c --- /dev/null +++ b/+datop/hstack_struct.m @@ -0,0 +1,66 @@ +function s = force_mergestruct(varargin) + +filler = missing; +isMerge = cellfun(@(x) isstruct(x), varargin); + +idx1_opts = find(~isMerge, 1, 'first'); + +if ~isempty(idx1_opts) + opts = varargin(idx1_opts:end); + + ii = 1; + while ii <= length(opts) + + optN = opts{ii}; + ii = ii + 1; + switch optN + + case {'fill_with'} + + filler = opts{ii}; + ii = ii + 1; + end + + end + + + s = varargin(1:idx1_opts-1); +else + + s = varargin; +end + + +try + + s = [s{:}]; + +catch ME + + switch ME.identifier + + case 'MATLAB:catenate:structFieldBad' + + + + fld_names = cellfun(@(x) string(fieldnames(x))', s, 'UniformOutput', false); + fld_names = unique([fld_names{:}]); + + for ii = 1:length(s) + + nan_fields = setdiff(fld_names, fieldnames(s{ii})); + for fldN = nan_fields + + s{ii}.(fldN) = filler; + + end + + end + + s = [s{:}]; + otherwise + rethrow(ME); + end + + +end \ No newline at end of file diff --git a/+datop/make_column.m b/+datop/make_column.m new file mode 100644 index 0000000..cc5cad9 --- /dev/null +++ b/+datop/make_column.m @@ -0,0 +1,5 @@ +function vec = make_column(vec) + +if isrow(vec), vec = vec'; end + +end \ No newline at end of file diff --git a/+datop/make_row.m b/+datop/make_row.m new file mode 100644 index 0000000..76c241b --- /dev/null +++ b/+datop/make_row.m @@ -0,0 +1,5 @@ +function vec = make_row(vec) + +if iscolumn(vec), vec = vec'; end + +end \ No newline at end of file diff --git a/+datop/struct_to_varargin.m b/+datop/struct_to_varargin.m new file mode 100644 index 0000000..67ddfe6 --- /dev/null +++ b/+datop/struct_to_varargin.m @@ -0,0 +1,22 @@ +function op = struct_to_varargin(s) + +assert(isstruct(s) && isscalar(s), "Input must be a struct array of length 1.") + +op = arrayfun(@(f_name, f_val) {f_name(:), handle_empty_val(f_val{:})}, string(fieldnames(s)), struct2cell(s), 'UniformOutput', false); + +op = horzcat(op{:})'; +end + +function op = handle_empty_val(f_val) + +if isempty(f_val) + + op = gen.empty_like(f_val); + +else + + op = f_val; + +end + +end \ No newline at end of file diff --git a/+num/absargmin.m b/+num/absargmin.m new file mode 100644 index 0000000..42cf77a --- /dev/null +++ b/+num/absargmin.m @@ -0,0 +1,10 @@ +function i = absargmin(varargin) +if nargin == 1 +% varargin = {varargin{1},1,varargin{2:end}}; +% else + varargin{2} = 1; +end +% [~, i] = min(abs(varargin{1}),varargin{2:end}); +[~, i] = mink(abs(varargin{1}),varargin{2:end}); + +end \ No newline at end of file diff --git a/+num/find_closest_integer_divisor_pair.m b/+num/find_closest_integer_divisor_pair.m new file mode 100644 index 0000000..9cffb68 --- /dev/null +++ b/+num/find_closest_integer_divisor_pair.m @@ -0,0 +1,12 @@ +function pairN = get_closest_integer_dividers(num) + +assert(isscalar(num) & isnumeric(num) & rem(num,1)==0, "num must be an integer!"); + +pairs = arrayfun(@(x) [x, num/x],1:sqrt(num),'UniformOutput',false); +pairs = vertcat(pairs{:}); +pairs = sort(pairs,2); +isInt = ~any(rem(pairs,1),2); +pairs = pairs(isInt,:); +pairN = pairs(gen.absargmin(diff(pairs,[],2)),:); + +end \ No newline at end of file diff --git a/+num/ifbetween.m b/+num/ifbetween.m new file mode 100644 index 0000000..800bfa4 --- /dev/null +++ b/+num/ifbetween.m @@ -0,0 +1,4 @@ +function i = ifbetween(a,vec) +i = a> vec(1) & a < vec(2); +end + diff --git a/+num/ifinrange.m b/+num/ifinrange.m new file mode 100644 index 0000000..ac31806 --- /dev/null +++ b/+num/ifinrange.m @@ -0,0 +1,5 @@ +function i = ifinrange(a, vec) + +i = a>=vec(1) & a <= vec(2); + +end \ No newline at end of file diff --git a/+num/ifwithin.m b/+num/ifwithin.m new file mode 100644 index 0000000..9160584 --- /dev/null +++ b/+num/ifwithin.m @@ -0,0 +1,5 @@ +function i = ifwithin(varargin) + +i = gen.ifinrange(varargin{:}); + +end \ No newline at end of file diff --git a/+stats/range.m b/+stats/range.m new file mode 100644 index 0000000..df21ddf --- /dev/null +++ b/+stats/range.m @@ -0,0 +1,15 @@ +function r = range(arr, dim) + +if nargin == 1, dim = 'all'; end + +if isnumeric(dim) + r = cat(dim,min(arr,[],dim), max(arr,[],dim)); +elseif strcmp(dim,"all") + + r = [min(arr,[],dim), max(arr,[],dim)]; + +else + error("dimension needs to be a numeric value or 'all'."); +end + +end \ No newline at end of file diff --git a/+stats/robust_z.m b/+stats/robust_z.m new file mode 100644 index 0000000..1e7b844 --- /dev/null +++ b/+stats/robust_z.m @@ -0,0 +1,63 @@ +function varargout = robust_z(arr, dim, var_method, varargin) + +%{ +Robust Z-score: +Calculated using the median and median absolute deviation (MAD).   +Formula: RobustZ= (x−median)/MAD +​ +The median and MAD are less affected by outliers, making the robust Z-score more reliable when dealing with data that may contain extreme values.   + +Median: The middle value in a sorted dataset.   +Median Absolute Deviation (MAD): The median of the absolute differences between each data point and the median. +   +%} + + +if nargin == 1 || isempty(dim) + + dim = 'all'; + +end + + +m = median(arr, dim, 'omitnan'); + +if nargin < 3 + + var_method = 'mad'; + +end + + +switch var_method + + case 'mad' + + var = median(abs(arr - m), dim, 'omitnan'); + + case 'iqr' + + var = iqr(arr, dim); + + case 'std' + + if isempty(varargin) + scalar = 1/1.349; % iqr calculated from normal standard deviation + else + scalar = varargin{1}; + end + + var = scalar * iqr(arr, dim); + + otherwise + error("Unknown method to compute variance. Pick one of the following: 'mad', 'robust_std', 'iqr'") +end + +varargout = cell(1, nargout); +varargout{1} = (arr - m) ./ var; + +if nargout > 1, varargout{2} = m; end +if nargout > 2, varargout{3} = var; end + +end + diff --git a/+util/Timer.m b/+util/Timer.m new file mode 100644 index 0000000..d6b36c6 --- /dev/null +++ b/+util/Timer.m @@ -0,0 +1,297 @@ +classdef Timer < matlab.mixin.Copyable % Inherits copy functionality + %{ +The Timer class provides a flexible and convenient way to measure time in your MATLAB programs. It offers functionality similar to the built-in tic and toc functions but with additional features for tracking multiple time intervals ("lapses") and generating detailed reports. + +Key Features: + +1. Start and Stop: The start() and stop() methods initiate and terminate the timer, respectively. +2. Lapses: The lapse() method allows you to record intermediate time points within a running timer. This is useful for tracking the duration of different sections of code or events within a process. +3. Duration and Intervals: The duration property provides the total elapsed time, while the intervals property gives the durations between consecutive lapses. Both are formatted as tables for easy readability. +4. Lags: The lags property provides the durations of each lapse relative to the start time. +5. Reporting: The report() method generates formatted output summarizing the elapsed time, intervals, or lags. +6. Copyable: The class inherits from matlab.mixin.Copyable, allowing you to create copies of Timer objects. + %} + % This class provides a simple timer functionality, similar to tic/toc, but + % with the ability to record multiple lapses and control output verbosity. + % MOz, Feb 25 + properties (SetAccess = protected) % Properties that can be set only within the class + t0 % Start time of the timer + tF % Stop time of the timer + tN (1,:) datetime = datetime.empty() % Array to store lapse times + end + + properties (Dependent) % Dependent properties (calculated on the fly) + duration % Formatted duration string + lags % lapse durations relative to t0 + intervals % lapse durations relative to tN-1 + n_lapse % Number of lapses recorded + + end + + properties (Dependent, Access = protected) % Dependent, accessible only within the class and its subclasses + raw_duration % Raw duration as a duration object + raw_intervals % Raw interval durations as an array of duration objects + raw_lags % Raw lag durations as an array of duration objects + end + + properties (Access = protected) % Private properties, accessible only within the class and its subclasses + isInitiated = false % Flag indicating if the timer has started + isTerminated = false % Flag indicating if the timer has stopped + end + + properties(Access = protected, Constant) % Constant properties, accessible only within the class and its subclasses + duration_types = ["days", "hours", "minutes", "seconds", "milliseconds"] + end + + methods % Methods of the Timer class + function self = Timer() % Constructor + % Creates a Timer object. + % + % Usage: + % timerObj = Timer(); + end + + function varargout = start(self, varargin) % Start the timer + % Starts the timer. Optionally displays a message. + % + % Usage: + % timerObj = start(timerObj); % Start the timer + % timerObj = start(timerObj, 'Starting the timer...'); % Start and display a message + + if self.isInitiated && ~self.isTerminated + error("The existing timer is being reset!"); + end + + self.t0 = datetime("now"); % Record the current time + self.isInitiated = true; % Set initiation flag + self.isTerminated = false; % Reset termination flag + + if nargout == 0, varargout = {}; + elseif nargout == 1, varargout{1} = self; + else, error("Too many output arguments!") + end + + if nargin > 1, fprintf(varargin{:}); end + end + + function varargout = stop(self, varargin) % Stop the timer + + if self.isInitiated && ~self.isTerminated % Check if timer was started + self.tF = datetime("now"); % Record the stop time + self.isTerminated = true; % Set termination flag + else + warning("No timers were initiated. Ignoring.") % Warning if timer wasn't started + return; + end + + if nargout == 0, varargout = {}; + elseif nargout == 1, varargout{1} = self; + else, error("Too many output arguments!") + end + + if nargin > 1, fprintf(varargin{:}); end + end + + function varargout = reset(self, varargin) + + if self.isInitiated && ~self.isTerminated + + self.stop(); + self.start(varargin{:}); + + else + warning("No timer was set to reset. Ignoring..."); + return + end + + if nargout == 0, varargout = {}; + elseif nargout == 1, varargout{1} = self; + else, error("Too many output arguments!") + end + + if nargin > 1, fprintf(varargin{:}); end + + end + + function varargout = lapse(self, varargin) % Record a lapse time + if nargin == 1, varargin{1} = ''; end + if self.isInitiated || ~self.isTerminated % Check if timer is running + self.tN(end+1) = datetime("now"); % Record lapse time + else + warning("No timers were initiated. Ignoring.") % Warning if timer wasn't started + varargin = {}; % prevent display + end + + if nargout == 0, varargout = {}; + elseif nargout == 1, varargout{1} = self; + else, error("Too many output arguments!") + end + + if nargin > 1, fprintf(varargin{:}); end + end + + function d = get.raw_duration(self) % Get raw duration + % Calculates the raw duration as a duration object. + + d = self.tF - self.t0; % Duration = stop time - start time + + end + + function d = get.duration(self) % Get formatted duration string + + d = self.dur2int(self.raw_duration); + d = self.dur2tbl(d); + + end + + function d = return_duration(self) + + d = self.raw_duration; + + end + + function i = get.raw_intervals(self) + + if isempty(self.tN), i = []; return; end + i = diff([self.t0, self.tN, self.tF]); + + end + + function i = get.intervals(self) + + if isempty(self.tN), i = self.raw_duration; return; end + i = self.dur2int(self.raw_intervals); + i = self.dur2tbl(i); + i = addvars(i, (1:self.n_lapse)', 'Before', 1, 'NewVariableNames', "rank"); % Insert before the first variable (index 1) + + end + + function d = return_intervals(self) + + d = self.raw_intervals; + + end + + function i = get.raw_lags(self) + + if isempty(self.tN), i = []; return; end + i = [self.tN, self.tF] - self.t0; + + end + + function i = get.lags(self) + + if isempty(self.tN), i = self.raw_duration; return; end + i = self.dur2int(self.raw_lags); + i = self.dur2tbl(i); + i = addvars(i, (1:self.n_lapse)', 'Before', 1, 'NewVariableNames', "rank"); % Insert before the first variable (index 1) + + end + + function d = return_lags(self) + + d = self.raw_lags; + + end + + function n = get.n_lapse(self) + + if isempty(self.tN), n = 0; return; end + n = length(self.tN) + 1; + + end + function varargout = report(self, type) + + if nargin == 1 + + if ~isempty(self.tN) + type = 'intervals'; + else, type = 'duration'; + end + + elseif ~any(strcmp(type, ["intervals", "lags", "duration"])) + error("Unrecognized type '%s' for report().", type) + + end + + dur = self.(type); + types = string(dur.Properties.VariableNames); + durs = table2array(dur); + + switch type + + case "intervals" + + s_init = "Elapsed times between intervals are:\n\n"; + + case "lags" + + s_init = "Elapsed times relative to the timer onset are:\n\n"; + + case "duration" + s_init = 'Elapsed time is '; + end + + if strcmp(type, "duration") + + s = sprintf("%s %s.\n",s_init, join(arrayfun(@(x,y) sprintf("%d %s",x,y), durs, types),', ')); + + else + + s = sprintf("%s%s",s_init, compose(join(compose("\t%d. %d %%s, %d %%s\n", durs(:,1), durs(:,2:end)),''),repmat(types(2:end),1, self.n_lapse))); + + end + + fprintf(s); + if nargout == 1, varargout{1} = s; end + + end + + + end + + methods(Access = protected) + + function t = dur2tbl(self, d) + + isIncld = any(d,1); + + t = array2table(d(:, isIncld), 'VariableNames', self.duration_types(isIncld)); + + end + + end + + methods (Access = protected, Static) + + function varargout = dur2int(dur) + + funcs = gen.Timer.duration_types; + durs = zeros(length(dur), length(funcs)); + for iFunc = 1:length(funcs) + + for iDur = 1:size(durs,1) + + [durN, dur_tmp] = gen.Timer.count_dur_by_type(dur(iDur), funcs(iFunc)); + dur(iDur) = dur_tmp; + durs(iDur, iFunc) = durN; + + end + + end + + varargout{1} = durs; + if nargout > 1, varargout{2} = dur; end + + end + + function [subdur, rem] = count_dur_by_type(dur, func) + + subdur = floor(feval(func,dur)); % Extract duration (e.g. seconds) + rem = dur - feval(func,subdur); % Subtract extracted time + + end + + end + +end \ No newline at end of file From 59263ddcfdb1f66386195a60376cf392a36aed0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20=C3=96zkan?= Date: Fri, 3 Oct 2025 10:01:17 -0400 Subject: [PATCH 2/2] Delete Timer.m --- +util/Timer.m | 297 -------------------------------------------------- 1 file changed, 297 deletions(-) delete mode 100644 +util/Timer.m diff --git a/+util/Timer.m b/+util/Timer.m deleted file mode 100644 index d6b36c6..0000000 --- a/+util/Timer.m +++ /dev/null @@ -1,297 +0,0 @@ -classdef Timer < matlab.mixin.Copyable % Inherits copy functionality - %{ -The Timer class provides a flexible and convenient way to measure time in your MATLAB programs. It offers functionality similar to the built-in tic and toc functions but with additional features for tracking multiple time intervals ("lapses") and generating detailed reports. - -Key Features: - -1. Start and Stop: The start() and stop() methods initiate and terminate the timer, respectively. -2. Lapses: The lapse() method allows you to record intermediate time points within a running timer. This is useful for tracking the duration of different sections of code or events within a process. -3. Duration and Intervals: The duration property provides the total elapsed time, while the intervals property gives the durations between consecutive lapses. Both are formatted as tables for easy readability. -4. Lags: The lags property provides the durations of each lapse relative to the start time. -5. Reporting: The report() method generates formatted output summarizing the elapsed time, intervals, or lags. -6. Copyable: The class inherits from matlab.mixin.Copyable, allowing you to create copies of Timer objects. - %} - % This class provides a simple timer functionality, similar to tic/toc, but - % with the ability to record multiple lapses and control output verbosity. - % MOz, Feb 25 - properties (SetAccess = protected) % Properties that can be set only within the class - t0 % Start time of the timer - tF % Stop time of the timer - tN (1,:) datetime = datetime.empty() % Array to store lapse times - end - - properties (Dependent) % Dependent properties (calculated on the fly) - duration % Formatted duration string - lags % lapse durations relative to t0 - intervals % lapse durations relative to tN-1 - n_lapse % Number of lapses recorded - - end - - properties (Dependent, Access = protected) % Dependent, accessible only within the class and its subclasses - raw_duration % Raw duration as a duration object - raw_intervals % Raw interval durations as an array of duration objects - raw_lags % Raw lag durations as an array of duration objects - end - - properties (Access = protected) % Private properties, accessible only within the class and its subclasses - isInitiated = false % Flag indicating if the timer has started - isTerminated = false % Flag indicating if the timer has stopped - end - - properties(Access = protected, Constant) % Constant properties, accessible only within the class and its subclasses - duration_types = ["days", "hours", "minutes", "seconds", "milliseconds"] - end - - methods % Methods of the Timer class - function self = Timer() % Constructor - % Creates a Timer object. - % - % Usage: - % timerObj = Timer(); - end - - function varargout = start(self, varargin) % Start the timer - % Starts the timer. Optionally displays a message. - % - % Usage: - % timerObj = start(timerObj); % Start the timer - % timerObj = start(timerObj, 'Starting the timer...'); % Start and display a message - - if self.isInitiated && ~self.isTerminated - error("The existing timer is being reset!"); - end - - self.t0 = datetime("now"); % Record the current time - self.isInitiated = true; % Set initiation flag - self.isTerminated = false; % Reset termination flag - - if nargout == 0, varargout = {}; - elseif nargout == 1, varargout{1} = self; - else, error("Too many output arguments!") - end - - if nargin > 1, fprintf(varargin{:}); end - end - - function varargout = stop(self, varargin) % Stop the timer - - if self.isInitiated && ~self.isTerminated % Check if timer was started - self.tF = datetime("now"); % Record the stop time - self.isTerminated = true; % Set termination flag - else - warning("No timers were initiated. Ignoring.") % Warning if timer wasn't started - return; - end - - if nargout == 0, varargout = {}; - elseif nargout == 1, varargout{1} = self; - else, error("Too many output arguments!") - end - - if nargin > 1, fprintf(varargin{:}); end - end - - function varargout = reset(self, varargin) - - if self.isInitiated && ~self.isTerminated - - self.stop(); - self.start(varargin{:}); - - else - warning("No timer was set to reset. Ignoring..."); - return - end - - if nargout == 0, varargout = {}; - elseif nargout == 1, varargout{1} = self; - else, error("Too many output arguments!") - end - - if nargin > 1, fprintf(varargin{:}); end - - end - - function varargout = lapse(self, varargin) % Record a lapse time - if nargin == 1, varargin{1} = ''; end - if self.isInitiated || ~self.isTerminated % Check if timer is running - self.tN(end+1) = datetime("now"); % Record lapse time - else - warning("No timers were initiated. Ignoring.") % Warning if timer wasn't started - varargin = {}; % prevent display - end - - if nargout == 0, varargout = {}; - elseif nargout == 1, varargout{1} = self; - else, error("Too many output arguments!") - end - - if nargin > 1, fprintf(varargin{:}); end - end - - function d = get.raw_duration(self) % Get raw duration - % Calculates the raw duration as a duration object. - - d = self.tF - self.t0; % Duration = stop time - start time - - end - - function d = get.duration(self) % Get formatted duration string - - d = self.dur2int(self.raw_duration); - d = self.dur2tbl(d); - - end - - function d = return_duration(self) - - d = self.raw_duration; - - end - - function i = get.raw_intervals(self) - - if isempty(self.tN), i = []; return; end - i = diff([self.t0, self.tN, self.tF]); - - end - - function i = get.intervals(self) - - if isempty(self.tN), i = self.raw_duration; return; end - i = self.dur2int(self.raw_intervals); - i = self.dur2tbl(i); - i = addvars(i, (1:self.n_lapse)', 'Before', 1, 'NewVariableNames', "rank"); % Insert before the first variable (index 1) - - end - - function d = return_intervals(self) - - d = self.raw_intervals; - - end - - function i = get.raw_lags(self) - - if isempty(self.tN), i = []; return; end - i = [self.tN, self.tF] - self.t0; - - end - - function i = get.lags(self) - - if isempty(self.tN), i = self.raw_duration; return; end - i = self.dur2int(self.raw_lags); - i = self.dur2tbl(i); - i = addvars(i, (1:self.n_lapse)', 'Before', 1, 'NewVariableNames', "rank"); % Insert before the first variable (index 1) - - end - - function d = return_lags(self) - - d = self.raw_lags; - - end - - function n = get.n_lapse(self) - - if isempty(self.tN), n = 0; return; end - n = length(self.tN) + 1; - - end - function varargout = report(self, type) - - if nargin == 1 - - if ~isempty(self.tN) - type = 'intervals'; - else, type = 'duration'; - end - - elseif ~any(strcmp(type, ["intervals", "lags", "duration"])) - error("Unrecognized type '%s' for report().", type) - - end - - dur = self.(type); - types = string(dur.Properties.VariableNames); - durs = table2array(dur); - - switch type - - case "intervals" - - s_init = "Elapsed times between intervals are:\n\n"; - - case "lags" - - s_init = "Elapsed times relative to the timer onset are:\n\n"; - - case "duration" - s_init = 'Elapsed time is '; - end - - if strcmp(type, "duration") - - s = sprintf("%s %s.\n",s_init, join(arrayfun(@(x,y) sprintf("%d %s",x,y), durs, types),', ')); - - else - - s = sprintf("%s%s",s_init, compose(join(compose("\t%d. %d %%s, %d %%s\n", durs(:,1), durs(:,2:end)),''),repmat(types(2:end),1, self.n_lapse))); - - end - - fprintf(s); - if nargout == 1, varargout{1} = s; end - - end - - - end - - methods(Access = protected) - - function t = dur2tbl(self, d) - - isIncld = any(d,1); - - t = array2table(d(:, isIncld), 'VariableNames', self.duration_types(isIncld)); - - end - - end - - methods (Access = protected, Static) - - function varargout = dur2int(dur) - - funcs = gen.Timer.duration_types; - durs = zeros(length(dur), length(funcs)); - for iFunc = 1:length(funcs) - - for iDur = 1:size(durs,1) - - [durN, dur_tmp] = gen.Timer.count_dur_by_type(dur(iDur), funcs(iFunc)); - dur(iDur) = dur_tmp; - durs(iDur, iFunc) = durN; - - end - - end - - varargout{1} = durs; - if nargout > 1, varargout{2} = dur; end - - end - - function [subdur, rem] = count_dur_by_type(dur, func) - - subdur = floor(feval(func,dur)); % Extract duration (e.g. seconds) - rem = dur - feval(func,subdur); % Subtract extracted time - - end - - end - -end \ No newline at end of file