function vec = prefnum(rng,def,mrg,varargin)
% Return a vector of Preferred Number Sequence values.
%
% (c) 2013 Stephen Cobeldick
%
% ### Function ###
%
% Return a vector of Preferred Number Sequence (PNS) values, selecting
% from any inbuilt PNS or defining custom numeric or function handle PNS.
%
% By default the vec values are the smallest sequence that encompasses the
% minimum and maximum values of input rng (a numeric scalar/vector/matrix).
% Optional input mrg selects a two-value margin around the min and max values.
%
% Syntax:
% vec = prefnum(rng,StrTok) % Select an inbuilt PNS using a string token.
% vec = prefnum(rng,NumVec) % Define a custom PNS using a numeric vector.
% vec = prefnum(rng,FunHdl) % Define a custom PNS using a function handle.
% vec = prefnum(rng, ... ,mrg) % Tight fit or margin around min&max of rng.
%
% See also HISTB HISTC HIST LOG10 PCHIP ROUND2SF ROUND2DP DATEROUND ROUND
%
% ### Examples ###
%
% prefnum([1,20],'E6')
% ans = [1,1.5,2.2,3.3,4.7,6.8,10,15,22]
%
% prefnum([1,20],'125',true)
% ans = [0.2,0.5,1,2,5,10,20,50,100]
%
% prefnum([1,20],[100,316])
% ans = [1,3.16,10,31.6]
%
% prefnum([1,20],@(x)3*x)
% ans = [0,3,6,9,12,15,18,21]
%
% ### Rounding to PNS Values ###
%
% X = [514,1.9;7.6,37] % Matrix of finite positive values to round.
%
% vec = prefnum(X,'E6',true) % Vector of 'E6' component values.
% [n,bin,edg] = histb(X,vec,'harmonic') % Place X-values into bins.
% vec(bin+1) % Use bin indices to select component values.
% ans = [470,2.2;6.8,33] % Best match to X, matching tolerances.
%
% vec = prefnum(X,'125',true) % Generate vector of '125' series.
% [n,bin,edg] = histb(X,vec,'geometric') % Place X-values into bins.
% vec(bin+1) % Use bin indices to select series values.
% ans = [500,2;10,50] % Best match, closest geometrically.
%
% vec = prefnum(X,@(v)5*v,true) % Generate vector of multiples of five.
% [n,bin,edg] = histb(X,vec,'arithmetic') % Place X-values into bins.
% vec(bin+1) % Use bin indices to select PNS values.
% ans = [515,0;10,35] % Best match, bin-edges at midpoints.
%
% # Find the paper size that fits the given dimension:
% Y = 234 % Want paper side >= this dimension.
% vec = prefnum(Y,'paperA',true) % Generate vector of paper edge sizes.
% [n,bin,edg]=histb(Y,vec,'position',true) % Place Y-value into bins.
% vec(bin+[-1,0]) % Use bin indices to select paper size.
% ans = [210,297] % A4 paper is >= dimension Y.
%
% ### Inbuilt PNS String Tokens (StrTok) ###
%
% Repeating PNS are marked with 'R', Complete PNS with 'C'. The range
% for Repeating PNS = 0<rng<Inf, and for Complete PNS = -Inf<=rng<=Inf.
%
% PNS Series: | StrTok Tokens: Range:
% -------------------------------------------------------------------------
% 1-2-5 | 125 | | | | | | | | R
% -------------------------------------------------------------------------
% Renard | R5 | R10 | R20 | R40 | R80 | | | | R
% |------|------|------|------|------|------|------|------|--
% | R'5 | R'10 | R'20 | R'40 | | | | | R
% |------|------|------|------|------|------|------|------|--
% | R"5 | R"10 | R"20 | | | | | | R
% -------------------------------------------------------------------------
% Electronic | E3 | E6 | E12 | E24 | E48 | E96 | E192 | | R
% -------------------------------------------------------------------------
% Screw Thread | M261 | M262 | | | | | | | C
% -------------------------------------------------------------------------
% Camera f-stop | f-full | f-half | f-third | f-quarter | C
% -------------------------------------------------------------------------
% Wire X-section| wire | | | | | | | | C
% -------------------------------------------------------------------------
% Pen Thickness | pen | | | | | | | | C
% -------------------------------------------------------------------------
% Paper Length | paperA | paperB | paperC | | | C
% =========================================================================
%
% PNS Series: | Definition:
% -------------------------------------------------------------------------
% 1-2-5 | [..,1,2,5,10,20,50,100,..] (= Renard R3 to one sig-fig)
% -------------------------------------------------------------------------
% Renard | ISO 3, ISO 17, ISO 497 (fuse ratings, transformer power)
% -------------------------------------------------------------------------
% Electronic | IEC 60063 (resistors, capacitors, inductors, zener diodes)
% -------------------------------------------------------------------------
% Screw Thread | ISO 261, ISO 262 (from M1 to M64 / from 1mm to 64mm)
% -------------------------------------------------------------------------
% Camera f-stop | lens focal ratio (from 0.7 to 256)
% -------------------------------------------------------------------------
% Wire X-section| IEC 60228 (wire cross-section areas from 0.5mm^2 to 1000mm^2)
% -------------------------------------------------------------------------
% Pen Thickness | [0.13,0.18,0.25,0.35,0.5,0.7,1,1.4,2] (ISO 128 line widths)
% -------------------------------------------------------------------------
% Paper Length | A, B & C series paper side lengths (mm) (ISO 216 & ISO 269)
% =========================================================================
%
% ### Custom PNS Numeric Vectors (NumVec) ###
%
% - NumVec element values must be strictly monotonic increasing.
%
% If the sequence digits repeat each decade (repeating):
% - NumVec element values must be positive and of the same order of magnitude.
% - An optimal NumVec = 10.^((0:N-1)/N), where N = numel(one_decade_of_PNS).
% - The values of rng must be finite and positive.
%
% If the sequence digits do not repeat (complete):
% - The first element value must be -Inf, the last element value must be Inf.
%
% ### Custom PNS Function Handles (FunHdl) ###
%
% - The function FunHdl(x) must be strictly monotonic increasing with x.
% - Returns FunHdl(x) that best match the InMat values, for integer x values.
% - Solution is found using "fzero", the optional inputs may be passed as well.
%
% ### Inputs & Output ###
%
% Inputs (*=default):
% rng = Numeric (any size), min&max values will be encompassed by the output vec.
% def = String Token, to select an inbuilt PNS (see tables above).
% = Numeric Vector, finite, positive, strictly monotonic increasing values.
% = Function Handle, a scalar function in one variable, monotonic increasing.
% mrg = Logical scalar, true/false* -> 2-value/tight margin around min&max of rng.
% If using a function handle defined PNS, the following are passed to "fzero":
% x0 = Numeric Scalar, an optional seed value, *mean(rng).
% opt = Struct, optional settings structure.
%
% Output:
% vec = Vector of PNS values ecompassing the min&max of rng, sequence defined by def.
%
% Inputs = (rng,def,mrg*,x0*,opt*)
% Output = vec
assert(isnumeric(rng),'Argument rng must be a numeric matrix/vector/scalar.')
rng = rng(:);
rmn = min(rng);
rmx = max(rng);
%
IsT = nargin<3||~mrg;
IsR = false;
%
if isnumeric(def) % Numeric
def = def(:);
assert(all(0<=diff(def)),'Numeric vector must be strictly monotonic increasing.')
IsI = isinf(def);
IsR = ~(IsI(1)||IsI(end));
if IsR % Repeating PNS
ord = floor(log10([min(def),max(def)]));
assert(0==diff(ord),'Repeating PNS numeric elements must be same order of magnitude.')
ord = ord(1);
else
vec = def;
end
%
elseif strncmp('E',def,1) % Electronic
def = PrNmElec(def);
ord = 2;
IsR = true;
%
elseif strncmp('R',def,1) % Renard
def = PrNmRnrd(def);
ord = 2;
IsR = true;
%
elseif strcmp('125',def) % 1-2-5
def = [1;2;5];
ord = 0;
IsR = true;
%
elseif strncmp('f-',def,2) % Camera f-stop
vec = PrNmFstop(def);
%
elseif strcmp('M261',def) % Metric screw thread
vec = [-Inf;1;1.2;1.4;1.6;1.8;2;2.5;3;3.5;4;5;6;7;8;10;12;...
14;16;18;20;22;24;27;30;33;36;39;42;45;48;52;56;60;64;Inf];
%
elseif strcmp('M262',def) % Metric screw thread
vec = [-Inf;1;1.2;1.6;2;2.5;3;4;5;6;8;10;12;16;20;24;30;36;42;48;56;64;Inf];
%
elseif strcmp('wire',def) % Wire cross-section
vec = [-Inf;0.5;0.75;1;1.5;2.5;4;6;10;16;25;35;50;70;...
95;120;150;185;240;300;400;500;630;800;1000;Inf];
%
elseif strcmp('pen',def) % Pen thickness
vec = [-Inf;0.13;0.18;0.25;0.35;0.5;0.7;1;1.4;2;Inf];
%
elseif strncmp('paper',def,5) % Paper Length
vec = PrNmPaper(def);
%
elseif isa(def,'function_handle') % Function Handle
ErSt = 'No finite and real solution found such that %s=%6.4f.';
% "fzero" optional arguments:
DfAr = {mean(rng),struct('Display','notify','TolX',eps,...
'FunValCheck','on','OutputFcn',[],'PlotFcns',[])};
DfAr(1:numel(varargin)) = varargin;
% Find min and max of PNS range:
VlN = floor(fzero(@(x)def(x)-rmn,DfAr{1},DfAr{2}));
assert(isfinite(VlN)&&isreal(VlN),ErSt,func2str(def),rmn)
VlX = ceil(fzero(@(x)def(x)-rmx,DfAr{1},DfAr{2}));
assert(isfinite(VlX)&&isreal(VlX),ErSt,func2str(def),rmx)
% PNS vector:
vec = arrayfun(def,VlN-2:2+VlX).';
%
else
error('def is not supported.')
end
%
if IsR % Repeating PNS
assert(rmn>0&&rmx>0,'The values of rng must be positive (repeating sequences only).')
% Order of magnitude required:
Pn = floor(log10(rmn));
Px = ceil(log10(rmx));
% Extrapolate PNS vector:
switch numel(def)
case 1
vec = (def*10.^(Pn-2:2+Px)).'/10^ord;
case 2
vec = def*10.^(Pn-1:1+Px);
vec = vec(:)/10^ord;
otherwise
vec = def*10.^(Pn:Px);
vec = [def(end-1:end).*10^(Pn-1);vec(:);def(1:2).*10^(Px+1)]/10^ord;
end
end
%
% Trim the PNS vector:
if IsT % Tight fit to rmn & rmx
vec = vec([rmn<vec(2:end);true]&[true;vec(1:end-1)<rmx]).';
else % 2-value margin around rmn and rmx
vec = vec([rmn<=vec(3:end);true;true]&[true;true;vec(1:end-2)<=rmx]).';
end
%
end
%--------------------------------------------------------------------------
function V = PrNmElec(def)
% Electronic PNS (IEC 60063)
%
switch def
case 'E3'
V =[100;220;470];
case 'E6'
V =[100;150;220;330;470;680];
case 'E12'
V =[100;120;150;180;220;270;330;390;470;560;680;820];
case 'E24'
V =[100;110;120;130;150;160;180;200;220;240;270;300;...
330;360;390;430;470;510;560;620;680;750;820;910];
case 'E48'
V =[100;105;110;115;121;127;133;140;147;154;162;169;...
178;187;196;205;215;226;237;249;261;274;287;301;...
316;332;348;365;383;402;422;442;464;487;511;536;...
562;590;619;649;681;715;750;787;825;866;909;953];
case 'E96'
V =[100;102;105;107;110;113;115;118;121;124;127;130;...
133;137;140;143;147;150;154;158;162;165;169;174;...
178;182;187;191;196;200;205;210;215;221;226;232;...
237;243;249;255;261;267;274;280;287;294;301;309;...
316;324;332;340;348;357;365;374;383;392;402;412;...
422;432;442;453;464;475;487;499;511;523;536;549;...
562;576;590;604;619;634;649;665;681;698;715;732;...
750;768;787;806;825;845;866;887;909;931;953;976];
case 'E192'
V =[100;101;102;104;105;106;107;109;110;111;113;114;...
115;117;118;120;121;123;124;126;127;129;130;132;...
133;135;137;138;140;142;143;145;147;149;150;152;...
154;156;158;160;162;164;165;167;169;172;174;176;...
178;180;182;184;187;189;191;193;196;198;200;203;...
205;208;210;213;215;218;221;223;226;229;232;234;...
237;240;243;246;249;252;255;258;261;264;267;271;...
274;277;280;284;287;291;294;298;301;305;309;312;...
316;320;324;328;332;336;340;344;348;352;357;361;...
365;370;374;379;383;388;392;397;402;407;412;417;...
422;427;432;437;442;448;453;459;464;470;475;481;...
487;493;499;505;511;517;523;530;536;542;549;556;...
562;569;576;583;590;597;604;612;619;626;634;642;...
649;657;665;673;681;690;698;706;715;723;732;741;...
750;759;768;777;787;796;806;816;825;835;845;856;...
866;876;887;898;909;920;931;942;953;965;976;988];
otherwise
error('Electronic token "%s" is not supported.',def)
end
%
end
%--------------------------------------------------------------------------
function V = PrNmRnrd(def)
% Renard PNS (ISO 3:1973, ISO 17:1973, ISO 497:1973)
%
switch def
case 'R5'
V =[100;160;250;400;630];
case 'R10'
V =[100;125;160;200;250;315;400;500;630;800];
case 'R20'
V =[100;112;125;140;160;180;200;224;250;280;...
315;355;400;450;500;560;630;710;800;900];
case 'R40'
V =[100;106;112;118;125;132;140;150;160;170;...
180;190;200;212;224;236;250;265;280;300;...
315;335;355;375;400;425;450;475;500;530;...
560;600;630;670;710;750;800;850;900;950];
case 'R80'
V =[100;103;106;109;112;115;118;122;125;128;...
132;136;140;145;150;155;160;165;170;175;...
180;185;190;195;200;206;212;218;224;230;...
236;243;250;258;265;272;280;290;300;307;...
315;325;335;345;355;365;375;387;400;412;...
425;437;450;462;475;487;500;515;530;545;...
560;580;600;615;630;650;670;690;710;730;...
750;775;800;825;850;875;900;925;950;975];
case 'R''5' % Not defined by ISO.
V =[100;160;250;400;630];
case 'R''10'
V =[100;125;160;200;250;320;400;500;630;800];
case 'R''20'
V =[100;110;125;140;160;180;200;220;250;280;...
320;360;400;450;500;560;630;710;800;900];
case 'R''40'
V =[100;105;110;120;125;130;140;150;160;170;...
180;190;200;210;220;240;250;260;280;300;...
320;340;360;380;400;420;450;480;500;530;...
560;600;630;670;710;750;800;850;900;950];
case 'R"5'
V =[100;150;250;400;600];
case 'R"10'
V =[100;120;150;200;250;300;400;500;600;800];
case 'R"20'
V =[100;110;120;140;150;180;200;220;250;280;...
300;350;400;450;500;550;600;700;800;900];
otherwise
error('Renard token "%s" is not supported.',def)
end
%
end
%--------------------------------------------------------------------------
function V = PrNmFstop(def)
% Camera f-stops
%
switch def
case 'f-full'
V =[-Inf;0.7;1;1.4;2;2.8;4;5.6;8;11;16;22;32;45;64;90;128;180;256;Inf];
case 'f-half'
V =[-Inf;0.7;0.8;1;1.2;1.4;1.7;2;2.4;2.8;3.3;4;4.8;5.6;6.7;8;9.5;...
11;13;16;19;22;27;32;38;45;53;64;76;90;128;152;180;215;256;Inf];
case 'f-third'
V =[-Inf;0.7;0.8;0.9;1;1.1;1.2;1.4;1.6;1.8;2;2.2;2.5;2.8;3.2;3.5;4;...
4.5;5;5.6;6.3;7.1;8;9;10;11;13;14;16;18;20;22;25;28;32;36;40;45;...
50;57;64;71;80;90;101;114;128;143;128;143;161;181;203;228;256;Inf];
case 'f-quarter'
V =[-Inf;0.7;0.8;0.85;0.9;1;1.1;1.2;1.3;1.4;1.5;1.7;1.8;2;2.2;2.4;...
2.6;2.8;3;3.4;3.7;4;4.4;4.8;5.2;5.6;6.2;6.7;7.3;8;8.7;9.5;10;...
11;12;13.5;15;16;17;19;21;22;25;27;29;32;35;38;41;45;49;54;...
59;64;70;83;90;99;107;128;140;152;166;181;197;215;235;256;Inf];
otherwise
error('Camera f-stop token "%s" is not supported.',def)
end
%
end
%--------------------------------------------------------------------------
function V = PrNmPaper(def)
% ISO 216 and ISO 296 paper side lengths for A, B and C series.
%
switch def
case 'paperA'
V =[-Inf;26;37;52;74;105;148;210;297;420;594;841;1189;Inf];
case 'paperB'
V =[-Inf;31;44;62;88;125;176;250;353;500;707;1000;1414;Inf];
case 'paperC'
V =[-Inf;28;40;57;81;114;162;229;324;458;648;917;1297;Inf];
otherwise
error('Paper side length token "%s" is not supported.',def)
end
%
end
%----------------------------------------------------------------------End!