classdef Anvin
    % Anvin is a RAID 6 style codec, with 1 parity and 1 extra check word
    % added. The codec can use a Galois field GF(2^m) and a message
    % consisting of blocks of length codeRank, each block 
    % consisting of m-bit words.

    properties
        m = 8;				% m in field GF(2^m)
        primitiveElement = gf(2,8);     % Generator of the field
        IDM;                            % Information dispersal matrix
        CHECK;                          % Check matrix
        codeRank = 3;            	% Number of message words per block
        codeLength = 5;          	% Code dimension: must be codeRank + 2
    end;
    
    methods
        function obj = Anvin(varargin)
	   if nargin > 0
	     % Parse arguments
	      if rem(nargin, 2) == 1
		error('Anvin:Anvin: Odd number of arguments, M-file %s.', mfilename);
	      end
	      for j=1:2:nargin
		k = varargin{j}; 
		v = varargin{j+1};
		switch(k)
		  case 'm'
		    obj.m = v;
		  case 'codeRank'
		    obj.codeRank = v;
		end
	      end
            end
	    obj.primitiveElement = gf(2,obj.m);
	    if obj.codeRank > 2^obj.m-2
                error('Code length must be at most 2^m-2');
            end
	    % Note: P = [1,1,1,...,1]
            P = gf(ones(1,obj.codeRank),obj.m);
	    % Note: Q = [1,g,g^2,1,...,g^(l-1)] where g is the primitive element
            Q = obj.primitiveElement .^ (0:obj.codeRank-1);
            obj.IDM=[eye(obj.codeRank);P;Q];
            obj.CHECK=[-[P;Q], eye(2)];
            obj.codeLength = obj.codeRank+2;
        end;
        
        function code = encode(obj, data)
            if length(size(data)) ~= 2
                error(['DATA must be a 2-dimensional array with ' ...
                    'data blocks in columns.']);
            end
            [codeRank,dataBlockCount]= size(data);
            if codeRank ~= obj.codeRank
                error(['Data CODELENGTH and IDM CODELENGTH must be ' ...
                    'the same.']);
            end
            code = obj.IDM * data;
        end
        
        function [data,fatal] = decode(obj, receivedCode)
            if length(size(receivedCode)) ~= 2
                error(['RECEIVEDCODE must be a 2-dimensional array with ' ...
                    'with codewords in columns.']);
            end
            [codeLength,codeWordCount] = size(receivedCode);
            if codeLength ~= obj.codeLength
                error(['Code dimension of the CODE and ' ...
                    'of IDM are not equal.']);
            end
            check = obj.CHECK * receivedCode;
            data = receivedCode;
            fatal = zeros(1,codeWordCount);
            j = zeros(1, codeWordCount);
            % Find indices of check vectors with both non-zero entries
            idx=find(check(1,:)~=0 & check(2,:)~=0);
            j(idx)=log(check(2,idx)./check(1,idx))+1;
            % Mark error locations out of range as fatal errors
            fatal(j > obj.codeLength) = 1;
            % Correct errors where both P and Q are ~=0
            % Set slices with fixed error location
            for l=1:obj.codeRank 
                idx1=(j==l);
                data(l,idx1) = data(l,idx1) - check(1,idx1);
            end
            % Correct errors where P==0
            idx=find(check(1,:)==0);
            l=obj.codeRank+2;
            data(l,idx) = data(l,idx) - check(2,idx);
            % Correct errors where Q==0
            idx=find(check(2,:)==0);
            l=obj.codeRank+1;
            data(l,idx) = data(l,idx) - check(1,idx);
        end
    end
end
