/*************************************************************************
* Copyright (C) 2017 Intel Corporation
*
* Licensed under the Apache License,  Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 	http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law  or agreed  to  in  writing,  software
* distributed under  the License  is  distributed  on  an  "AS IS"  BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the  specific  language  governing  permissions  and
* limitations under the License.
*************************************************************************/

/* 
// 
//  Purpose:
//     Cryptography Primitive. Modular Arithmetic Engine. General Functionality
// 
//  Contents:
//        alm_mont_inv_ct()
//
*/

#include "owndefs.h"
#include "owncp.h"
#include "pcpbnumisc.h"
#include "pcpbnuarith.h"
#include "gsmodstuff.h"
#include "pcpmask_ct.h"

/*
// almost Montgomery Inverse
//
// returns (k,r), r = (1/a)*(2^k) mod m
//
// (constant-execution-time version)
*/
IPP_OWN_DEFN (int, alm_mont_inv_ct, (BNU_CHUNK_T* pr, const BNU_CHUNK_T* pa, gsModEngine* pME))
{
   const BNU_CHUNK_T* pm = MOD_MODULUS(pME);
   int mLen = MOD_LEN(pME);
   int modulusBitSize = MOD_BITSIZE(pME);

   const int polLength  = 6;
   BNU_CHUNK_T* pBuffer = gsModPoolAlloc(pME, polLength);
   if(NULL == pBuffer)
      return 0; // zero return value is handled by the calling function
      
   BNU_CHUNK_T* pu = pBuffer;
   BNU_CHUNK_T* ps = pu+mLen;
   BNU_CHUNK_T* pv = ps+mLen;
   BNU_CHUNK_T* pt = pv+mLen;

   BNU_CHUNK_T* px = pt+mLen;
   BNU_CHUNK_T* py = px+mLen;

   int k = 0;
   int i;
   BNU_CHUNK_T ext = 0;

   // u=modulus, s=1 and v=a, t=0,
   COPY_BNU(pu, pm, mLen);
   ZEXPAND_BNU(ps, 0, mLen); ps[0] = 1;
   COPY_BNU(pv, pa, mLen);
   ZEXPAND_BNU(pt, 0, mLen);

   for(i=0; i<2*modulusBitSize; i++) {
      /* update mask - update = (v==0)? 0xFF : 0 */
      BNU_CHUNK_T update = ~cpIsGFpElemEquChunk_ct(pv, mLen, 0);
      /* temporary masks */
      BNU_CHUNK_T m, mm;

      /* compute in advance r = s+t */
      cpAdd_BNU(pr, ps, pt, mLen);

      /*
      // update or keep current u, s, v, t
      */

      /* if(isEven(u)) { u=u/2; s=2*s; } 1-st branch */
      m = update & cpIsEven_ct(pu[0]);
      cpLSR_BNU(px, pu, mLen, 1);
      cpAdd_BNU(py, ps, ps, mLen);
      cpMaskedReplace_ct(pu, px, mLen, m);
      cpMaskedReplace_ct(ps, py, mLen, m);

      /* else if(isEven(v)) { v=v/2; t=2*t; } 2-nd branch */
      mm = update & ~m & cpIsEven_ct(pv[0]);
      cpLSR_BNU(px, pv, mLen, 1);
      cpAdd_BNU(py, pt, pt, mLen);
      cpMaskedReplace_ct(pv, px, mLen, mm);
      cpMaskedReplace_ct(pt, py, mLen, mm);

      m |= mm; /* if fall in the 1-st of 2-nf branches m=FF.. else m=0 */

      /* else if(v>=u) { v=(v-u)/2; s=s+t; t=2*t;} 3-st branch */
      mm = cpSub_BNU(px, pv, pu, mLen);
      mm = cpIsZero_ct(mm);
      mm = update & ~m & mm;
      cpLSR_BNU(px, px, mLen, 1);
      ext += cpAdd_BNU(py, pt, pt, mLen) & mm;
      cpMaskedReplace_ct(pv, px, mLen, mm);
      cpMaskedReplace_ct(ps, pr, mLen, mm);
      cpMaskedReplace_ct(pt, py, mLen, mm);

      /* else { u=(u-v)/2; t= t+s; s=2*s; } 4-rd branch*/
      cpSub_BNU(px, pu, pv, mLen);
      mm = update & ~m & ~mm;
      cpLSR_BNU(px, px, mLen, 1);
      cpAdd_BNU(py, ps, ps, mLen);
      cpMaskedReplace_ct(pu, px, mLen, mm);
      cpMaskedReplace_ct(pt, pr, mLen, mm);
      cpMaskedReplace_ct(ps, py, mLen, mm);

      /* update or keep current k */
      k = ((k+1) & (Ipp32s)update) | (k & (Ipp32s)~update);
   }

   /*
   // r = (t>mod)? t-mod : t;
   // r = mod - t;
   */
   ext -= cpSub_BNU(pr, pt, pm, mLen);
   cpMaskedReplace_ct(pr, pt, mLen, ~cpIsZero_ct(ext));
   cpSub_BNU(pr, pm, pr, mLen);

   /* test if inversion not found (k=0) */
   k &= cpIsGFpElemEquChunk_ct(pu, mLen, 1);

   gsModPoolFree(pME, polLength);
   return k;
}
