function c = gfconv(varargin)
%GFCONV Multiply polynomials over a Galois field.
%   C = GFCONV(A, B) multiplies two GF(2) polynomials A and B.
%   The polynomial degree of the resulting GF(2) polynomial C equals
%   the degree of A plus the degree of B.
%
%   A and B can be specified as polynomial strings. C is a row vector that 
%   specifies the polynomial coefficients in order of ascending powers; A
%   and B can alternatively be specified in the same manner as C.
%
%   C = GFCONV(A, B, P) multiplies the two GF(P) polynomials, A and B,
%   where P is a scalar prime number.
%
%   C = GFCONV(A, B, FIELD) multiplies two GF(P^M) polynomials, where FIELD
%   is a matrix that contains the M-tuple of all elements in GF(P^M). P is
%   a prime number and M is a positive integer. To generate the M-tuple of
%   all elements in GF(P^M), use FIELD = GFTUPLE([-1:P^M-2]', M, P).
%
%   In this syntax, each coefficient is specified in exponential format,
%   that is, [-Inf, 0, 1, 2, ...] represent the field elements
%   [0, 1, alpha, alpha^2, ...] in GF(P^M).
%
%   C = GFCONV(STRPOLYS) multiplies all Galois polynomial strings contained
%   in the cell array or string vector STRPOLYS or in a text file, whose
%   name is given by the string STRPOLYS. The degree of the resulting
%   polynomial equals the sum of the degrees of each polynomial. C is a
%   vector of polynomial coefficients in order of ascending powers.
%
%   C = GFCONV(STRPOLYS, P) uses Galois field GF(P); if omitted, GF(2) is
%   assumed.
%
%   Note: This function performs computations in GF(P^M) where P is prime.
%   To work in GF(2^M), you can also use the CONV function with Galois
%   arrays.
%
%   Example 1:
%       % In GF(5): (1+ 2x+ 3x^2+ 4x^3)*(1+ x) = (1+ 3x+ 2x^3+ 4x^4)
%       c1 = gfconv('1+ 2x+ 3x^2+ 4x^3', '1 + x', 5)
%       c2 = gfconv([1 2 3 4], [1 1], 5)
%
%   Example 2: 
%       % In GF(2^4):
%       field = gftuple([-1:2^4-2]', 4, 2);
%       c = gfconv('1+ 2x+ 3x^2+ 4x^3 + 5x^4', '1 + x',field)
%
%   Example 3:
%      % Compute generator polynomial for DVB-S2 "short FEC" standard.
%      %
%      % GP is found by multiplying a dozen polynomials listed in the
%      % DVB-S2 specification document, which were copied and pasted into
%      % the text file 'dvbs2_fecshort.txt':
%
%      --------------------------------------------------------------------
%      # DVB-S2 spec for FEC: short BCH polynomials
%      # https://www.dvb.org/standards/dvb-s2
%      #
%      g1(x) 1+x+x3+x5+x14
%      g2(x) 1+x6+x8+x11+x14
%      g3(x) 1+x+x2+x6+x9+x10+x14
%      g4(x) 1+x4+x7+x8+x10+x12+x14
%      g5(x) 1+x2+x4+x6+x8+x9+x11+x13+x14
%      g6(x) 1+x3+x7+x8+x9+x13+x14
%      g7(x) 1+x2+x5+x6+x7+x10+x11+x13+x14
%      g8(x) 1+x5+x8+x9+x10+x11+x14
%      g9(x) 1+x+x2+x3+x9+x10+x14
%      g10(x) 1+x3+x6+x9+x11+x12+x14
%      g11(x) 1+x4+x11+x12+x14
%      g12(x) 1+x+x2+x3+x5+x6+x7+x8+x10+x13+x14
%      --------------------------------------------------------------------
%
%      GP   = GFCONV('dvbs2_fecshort.txt');
%      gfpretty(GP)    % DVB-S2 for t=12
%
%   Example 4:
%      % Multiply several polynomials passed as a cell array:
%      C = {'1+x+x3+x5+x14', '1+x6+x8+x11+x14', '1+x+x2+x6+x9+x10+x14'};
%      GP = gfconv(C); % DVB-S2 for t=3
%      gfpretty(GP)
%
%   See also GFDECONV, GFADD, GFSUB, GFTUPLE, GFMUL, GFPRETTY.

%   Copyright 1996-2018 The MathWorks, Inc.

    if (nargin == 1 || nargin == 2) && ...
            comm.internal.utilities.isCharOrStringScalar(varargin{1}) && exist(varargin{1}, 'file') == 2 
        % the first argument is a filename

        % Import polynomials from file:
        s = StringWriter;
        % Read the entire file, and store it to s:
        s.readfile( varargin{1} );
        % Split each row into a separate string. Replace filename with a
        % cell array containing all rows:
        varargin{1} = cellstr(s);
        
        c = gfconvall(varargin{:});

    elseif (nargin == 1 || nargin == 2) && ...
            (iscell(varargin{1}) || (isstring(varargin{1}) && length(varargin{1})>1)) 
        % the first argument is a cell array or a string vector
    
        if (iscell(varargin{1}) && any( cellfun(@ischar, varargin{1}) == 0 ) )
            id = 'comm:gf_conv:CellMustHaveStrings';
            error(id, getString(message(id)));
        end
        
        c = gfconvall(varargin{:});
        
    elseif nargin >= 2
        % the first two arguments are distinct polynomials

        % convert human-readable polynomials to equivalent numeric form
        if comm.internal.utilities.isCharOrStringScalar(varargin{1}) % A
            varargin{1} = commstr2poly(varargin{1}, 'ascending');
        end
        if comm.internal.utilities.isCharOrStringScalar(varargin{2}) % B
            varargin{2} = commstr2poly(varargin{2}, 'ascending');
        end
        
        c = gfconv_mex(varargin{:});

    else
        id = 'comm:gf_conv:WrongInputs';
        error(id, getString(message(id)));
    end

function c = gfconvall(V, P)

    % Convert polynomial text to coefficient vectors:
    if nargin < 2
        P = 2; % default to GF(2)
    end

    % convert each human-readable polynomial of the cell array to a binary
    % representation of ascending powers:
    t = commstr2poly(V);

    % remove entries that do not represent a polynomial:
    t(cellfun(@isempty,t)) = [];

    % Multiply each polynomial together:
    N = numel(t);
    if N == 0 % no polys found, return empty matrix:
        c = [];
    else
        c = 1;
        for i = 1:N
            % gfconv multiplies 2 polynomials over GF(P):
            c = gfconv(c, t{i}, P);
        end
    end
