function [DataType,IsScaledDouble] = fixdt( varargin )
%FIXDT Create an object describing a fixed-point or floating-point data type
%
% This data type object can be passed to Simulink blocks that support 
% fixed-point data types.  
%
% Usage 1: Fixed-Point Data Type With Unspecified scaling
%          Scaling would typically be determined by another block parameter.
%
%   FIXDT( Signed, WordLength )
%
% Usage 2: Fixed-Point Data Type With Binary point scaling
%
%   FIXDT( Signed, WordLength, FractionLength )
%
% Usage 3: Slope and Bias scaling
%
%   FIXDT( Signed, WordLength, TotalSlope, Bias )
%  or
%   FIXDT( Signed, WordLength, SlopeAdjustmentFactor, FixedExponent, Bias )
%
% Usage 4: Data Type Name String
%
%   FIXDT( DataTypeNameString )
%  or
%   [DataTypeObject,IsScaledDouble] = FIXDT( DataTypeNameString )
%     
%   The data type name string is the same string that would be displayed on 
%   signal lines in a Simulink model.  The optional setting to display port 
%   data types is found under the Simulink Format menu.
%
%   Examples using standard data types:
%
%      FIXDT('double')
%      FIXDT('single')
%      FIXDT('uint8')
%      FIXDT('uint16')
%      FIXDT('uint32')
%      FIXDT('int8')
%      FIXDT('int16')
%      FIXDT('int32')
%      FIXDT('boolean')
%
%   Key to fixed-point data type names: 
%
%      Simulink data type names are required to be valid matlab 
%      identifiers with less than 32 characters.  Fixed-point data
%      types are encoded using the following rules.
%          
%      Container
%
%        'ufix#'  unsigned with # bits  Ex. ufix3   is unsigned   3 bits
%        'sfix#'  signed   with # bits  Ex. sfix128 is signed   128 bits
%        'flts#'  scaled double data type override of sfix#
%        'fltu#'  scaled double data type override of ufix#
%
%      Number encoding
%
%        'n'      minus sign,           Ex. 'n31' equals -31
%        'p'      decimal point         Ex. '1p5' equals 1.5
%        'e'      power of 10 exponent  Ex. '125e18' equals 125*(10^(18))
%
%      Scaling Terms from the fixed-point scaling equation
% 
%           RealWorldValue = S * StoredInteger + B
%         or
%           RealWorldValue = F * 2^E * StoredInteger + B
%
%        'E'      FixedExponent           if S not given, default is 0
%        'F'      SlopeAdjustmentFactor   if S not given, default is 1
%        'S'      TotalSlope              if E not given, default is 1
%        'B'      Bias                    default 0
%
%     Examples using integers with non-standard number of bits
%
%        FIXDT('ufix1')       Unsigned  1 bit
%        FIXDT('sfix77')      Signed   77 bits
%
%     Examples using binary point scaling
%
%        FIXDT('sfix32_En31')    Fraction length 31  
%
%     Examples using slope and bias scaling
%
%        FIXDT('ufix16_S5')          TotalSlope 5 
%        FIXDT('sfix16_B7')          Bias 7
%        FIXDT('ufix16_F1p5_En50')   SlopeAdjustmentFactor 1.5  FixedExponent -50
%        FIXDT('ufix16_S5_B7')       TotalSlope 5, Bias 7
%        FIXDT('sfix8_Bn125e18')     Bias -125*10^18
%
%   Scaled Doubles
%
%     Scaled doubles data types are a testing and debugging feature.  Scaled
%     doubles occur when two conditions are met.  First, an integer or 
%     fixed-point data type is entered into a Simulink  block's mask.  Second,
%     the dominant parent subsystem has data type override setting of scaled 
%     doubles.  When this happens, a data type like 'sfix16_En7' is overridden
%     with a scaled doubles data type 'flts16_En7'.  
%        The first output of FIXDT will be the same whether the original 
%     data type 'sfix16_En7' is passed in or it's scaled doubles version
%     'flts16_En7' is passed in.  The optional second output argument 
%     is true if and only if the input is a scaled doubles data type.
%
% Usage 5: Data Type Object converted to eval ready string
%
%   stringReadyForEval = FIXDT( DataTypeObject )
%
%   Example
%       dataTypeObject   = fixdt(1,16,15)
%       stringReadyForEval = fixdt(dataTypeObject)
%       eval(stringReadyForEval)
%   The string that is returned is useful for configuring data types on blocks.
%       set_param(gcb,'OutDataType',stringReadyForEval)
%
  
% See also SFIX, UFIX, SINT, UINT, SFRAC, UFRAC, FLOAT.
 
% Copyright 1994-2003 The MathWorks, Inc.
% $Revision $  
% $Date: 2004/10/06 14:01:49 $

IsScaledDouble = false;

DataType = Simulink.NumericType;

if nargin >= 2
    
  Signed = varargin{1} ~= 0;
  
  WordLength = varargin{2};
  
  if nargin <= 2
    
    DataType.DataTypeMode = 'Fixed-point: unspecified scaling';
    
    DataType.Signed = Signed;
    
    DataType.WordLength = WordLength;
    
  elseif nargin <= 3
    
    DataType.DataTypeMode = 'Fixed-point: binary point scaling';
    
    DataType.Signed = Signed;
    
    DataType.WordLength = WordLength;
    
    DataType.FixedExponent = -varargin{3};
    
  else
    
    DataType.DataTypeMode = 'Fixed-point: slope and bias scaling';
    
    DataType.Signed = Signed;
    
    DataType.WordLength = WordLength;
    
    if nargin <= 4
      
      TotalSlope = varargin{3};
      Bias       = varargin{4};
    else
      TotalSlope = varargin{3} * 2^varargin{4};
      Bias       = varargin{5};
    end
    
    [fff,eee] = log2( TotalSlope );
    
    fff = 2 * fff;
    eee = eee - 1;
    
    DataType.FixedExponent = eee;
    
    DataType.SlopeAdjustmentFactor = fff;
    
    DataType.Bias = Bias;
  end
  
else
 
  firstInputArg = varargin{1};
  
  if ischar(firstInputArg)
    
    dataTypeNameStr = firstInputArg;
    
    switch lower(dataTypeNameStr)
      
     case 'double'
      DataType.DataTypeMode = 'Double';
      
     case 'single'
      DataType.DataTypeMode = 'Single';
      
     case 'float'
      DataType.DataTypeMode = 'Single';
      
     case 'boolean'
      DataType.DataTypeMode = 'Boolean';
      
     case 'bool'
      DataType.DataTypeMode = 'Boolean';
      
     case 'int32'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = true;
      DataType.WordLength = 32;
      DataType.FixedExponent = 0;
      
     case 'int16'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = true;
      DataType.WordLength = 16;
      DataType.FixedExponent = 0;
      
     case 'int8'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = true;
      DataType.WordLength = 8;
      DataType.FixedExponent = 0;
      
     case 'uint32'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = false;
      DataType.WordLength = 32;
      DataType.FixedExponent = 0;
      
     case 'uint16'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = false;
      DataType.WordLength = 16;
      DataType.FixedExponent = 0;
      
     case 'uint8'
      DataType.DataTypeMode = 'Fixed-point: binary point scaling';
      DataType.Signed = false;
      DataType.WordLength = 8;
      DataType.FixedExponent = 0;
      
     otherwise
      
      if (strncmp(dataTypeNameStr, 'sfix', 4) || ...
          strncmp(dataTypeNameStr, 'ufix', 4) || ...
          strncmp(dataTypeNameStr, 'flt',  3))
        
        [DataType,IsScaledDouble] = ResolveFixPtType(dataTypeNameStr);
        
      else
        error('simulink:fixedpoint:invaliddatatype','Unrecognized data type name string.');
      end
    end
  
  elseif strcmp(class(firstInputArg), 'Simulink.NumericType')
  
    [DataType,IsScaledDouble] = DataTypeObjToStrForEval(firstInputArg);
    
  else
    error('simulink:fixedpoint:invaliddatatype','If only one input argument is supplied to fixdt(), it must be a string.');
  end
end

function [DataType,IsScaledDouble] = ResolveFixPtType(dataTypeNameStr)
  
  DataType = Simulink.NumericType;

  pos = 1;
  end_pos = length(dataTypeNameStr);
  
  signed   = 0;
  slope    = 1;
  fraction = 1;
  exponent = 0;
  bias     = 0;
  IsScaledDouble = false;
  
  switch dataTypeNameStr(pos)
   case 's'
    signed = 1;
    pos = 5;
   case 'u'
    signed = 0;
    pos = 5;
   case 'f'
    pos = 4;
    IsScaledDouble = true;
    if (dataTypeNameStr(pos) == 's')
      signed  = 1;
      pos = 5;
    elseif (dataTypeNameStr(pos) == 'u')
      pos = 5;
    end
   otherwise
    error('simulink:fixedpoint:invaliddatatype','Unrecognized data type name string.');
  end
  
  sep = findstr(dataTypeNameStr(pos:end), '_');
  
  if isempty(sep)
    next_pos = end_pos;
  else
    next_pos = pos+sep(1)-2;   
  end
   
  try 
    WordLength = eval(dataTypeNameStr(pos:next_pos));
  catch
    WordLength = 0;
  end
  
  pos = next_pos + 2;
  
  while (pos < end_pos) 
    sep = findstr(dataTypeNameStr(pos:end), '_');
    
    if isempty(sep)
      next_pos = end_pos;
    else
      next_pos = pos+sep(1)-2;
    end 
  
    switch dataTypeNameStr(pos)
     case 'S'
      slope = ...
	  eval(strrep(strrep(dataTypeNameStr(pos+1: next_pos),'p','.'),'n','-'));
     case 'E'
      exponent = ...
	  eval(strrep(strrep(dataTypeNameStr(pos+1: next_pos),'p','.'),'n','-'));
     case 'B'
      bias = ...
	  eval(strrep(strrep(dataTypeNameStr(pos+1: next_pos),'p','.'),'n','-'));
     case 'F'
      fraction = ...
	  eval(strrep(strrep(dataTypeNameStr(pos+1: next_pos),'p','.'),'n','-'));
     otherwise
      error('simulink:fixedpoint:invaliddatatype',['(ResolveFixPtType) Don''t know what to do with "', dataTypeNameStr(pos:end), '"']);
    end
    pos = next_pos + 2;
  end

  if ( slope == 1 && fraction == 1 && bias == 0 )
    
    DataType.DataTypeMode = 'Fixed-point: binary point scaling';
    
    DataType.Signed = signed ~= 0;
    
    DataType.WordLength = WordLength;
    
    DataType.FixedExponent = exponent;
    
  else
    
    DataType.DataTypeMode = 'Fixed-point: slope and bias scaling';
    
    DataType.Signed = signed ~= 0;
    
    DataType.WordLength = WordLength;
    
    TotalSlope = slope * fraction * 2^exponent;
    
    [fff,eee] = log2( TotalSlope );
    
    fff = 2 * fff;
    eee = eee - 1;
    
    DataType.FixedExponent = eee;
    
    DataType.SlopeAdjustmentFactor = fff;
    
    DataType.Bias = bias;
    
  end


function [strForEval,IsScaledDouble] = DataTypeObjToStrForEval(dataTypeObj)

  IsScaledDouble = 1;
  
  switch dataTypeObj.DataTypeMode

   case 'Fixed-point: unspecified scaling'

    strForEval = sprintf('fixdt(%d,%d)', ...
                         dataTypeObj.IsSigned, ...
                         dataTypeObj.WordLength);
    
   case 'Fixed-point: slope and bias scaling'

    strForEval = sprintf('fixdt(%d,%d,%s,%d,%s)', ...
                         dataTypeObj.IsSigned, ...
                         dataTypeObj.WordLength, ...
                         num2str(dataTypeObj.SlopeAdjustmentFactor,17), ...
                         dataTypeObj.FixedExponent, ...
                         num2str(dataTypeObj.Bias,17));
    
   case 'Fixed-point: binary point scaling'

    strForEval = sprintf('fixdt(%d,%d,%d)', ...
                         dataTypeObj.IsSigned, ...
                         dataTypeObj.WordLength, ...
                         dataTypeObj.FractionLength);
   
   case 'Double'

    strForEval = 'fixdt(''double'')';
   
   case 'Single'
    
    strForEval = 'fixdt(''single'')';
   
   case 'Boolean'

    strForEval = 'fixdt(''boolean'')';
   
   otherwise

    error('simulink:fixedpoint:invaliddatatype','Unrecognized data type object.');
  end  

  