github.com/cloudflare/circl@v1.5.0/sign/mldsa/mldsa87/internal/rounding.go (about)

     1  // Code generated from mode3/internal/rounding.go by gen.go
     2  
     3  package internal
     4  
     5  import (
     6  	common "github.com/cloudflare/circl/sign/internal/dilithium"
     7  )
     8  
     9  // Splits 0 ≤ a < q into a₀ and a₁ with a = a₁*α + a₀ with -α/2 < a₀ ≤ α/2,
    10  // except for when we would have a₁ = (q-1)/α in which case a₁=0 is taken
    11  // and -α/2 ≤ a₀ < 0.  Returns a₀ + q.  Note 0 ≤ a₁ < (q-1)/α.
    12  // Recall α = 2γ₂.
    13  func decompose(a uint32) (a0plusQ, a1 uint32) {
    14  	// a₁ = ⌈a / 128⌉
    15  	a1 = (a + 127) >> 7
    16  
    17  	if Alpha == 523776 {
    18  		// 1025/2²² is close enough to 1/4092 so that a₁
    19  		// becomes a/α rounded down.
    20  		a1 = ((a1*1025 + (1 << 21)) >> 22)
    21  
    22  		// For the corner-case a₁ = (q-1)/α = 16, we have to set a₁=0.
    23  		a1 &= 15
    24  	} else if Alpha == 190464 {
    25  		// 1488/2²⁴ is close enough to 1/1488 so that a₁
    26  		// becomes a/α rounded down.
    27  		a1 = ((a1 * 11275) + (1 << 23)) >> 24
    28  
    29  		// For the corner-case a₁ = (q-1)/α = 44, we have to set a₁=0.
    30  		a1 ^= uint32(int32(43-a1)>>31) & a1
    31  	} else {
    32  		panic("unsupported α")
    33  	}
    34  
    35  	a0plusQ = a - a1*Alpha
    36  
    37  	// In the corner-case, when we set a₁=0, we will incorrectly
    38  	// have a₀ > (q-1)/2 and we'll need to subtract q.  As we
    39  	// return a₀ + q, that comes down to adding q if a₀ < (q-1)/2.
    40  	a0plusQ += uint32(int32(a0plusQ-(common.Q-1)/2)>>31) & common.Q
    41  
    42  	return
    43  }
    44  
    45  // Assume 0 ≤ r, f < Q with ‖f‖_∞ ≤ α/2.  Decompose r as r = r1*α + r0 as
    46  // computed by decompose().  Write r' := r - f (mod Q).  Now, decompose
    47  // r'=r-f again as  r' = r'1*α + r'0 using decompose().  As f is small, we
    48  // have r'1 = r1 + h, where h ∈ {-1, 0, 1}.  makeHint() computes |h|
    49  // given z0 := r0 - f (mod Q) and r1.  With |h|, which is called the hint,
    50  // we can reconstruct r1 using only r' = r - f, which is done by useHint().
    51  // To wit:
    52  //
    53  //	useHint( r - f, makeHint( r0 - f, r1 ) ) = r1.
    54  //
    55  // Assumes 0 ≤ z0 < Q.
    56  func makeHint(z0, r1 uint32) uint32 {
    57  	// If -α/2 < r0 - f ≤ α/2, then r1*α + r0 - f is a valid decomposition of r'
    58  	// with the restrictions of decompose() and so r'1 = r1.  So the hint
    59  	// should be 0. This is covered by the first two inequalities.
    60  	// There is one other case: if r0 - f = -α/2, then r1*α + r0 - f is also
    61  	// a valid decomposition if r1 = 0.  In the other cases a one is carried
    62  	// and the hint should be 1.
    63  	if z0 <= Gamma2 || z0 > common.Q-Gamma2 || (z0 == common.Q-Gamma2 && r1 == 0) {
    64  		return 0
    65  	}
    66  	return 1
    67  }
    68  
    69  // Uses the hint created by makeHint() to reconstruct r1 from r'=r-f; see
    70  // documentation of makeHint() for context.
    71  // Assumes 0 ≤ r' < Q.
    72  func useHint(rp uint32, hint uint32) uint32 {
    73  	rp0plusQ, rp1 := decompose(rp)
    74  	if hint == 0 {
    75  		return rp1
    76  	}
    77  	if rp0plusQ > common.Q {
    78  		return (rp1 + 1) & 15
    79  	}
    80  	return (rp1 - 1) & 15
    81  }
    82  
    83  // Sets p to the hint polynomial for p0 the modified low bits and p1
    84  // the unmodified high bits --- see makeHint().
    85  //
    86  // Returns the number of ones in the hint polynomial.
    87  func PolyMakeHint(p, p0, p1 *common.Poly) (pop uint32) {
    88  	for i := 0; i < common.N; i++ {
    89  		h := makeHint(p0[i], p1[i])
    90  		pop += h
    91  		p[i] = h
    92  	}
    93  	return
    94  }
    95  
    96  // Computes corrections to the high bits of the polynomial q according
    97  // to the hints in h and sets p to the corrected high bits.  Returns p.
    98  func PolyUseHint(p, q, hint *common.Poly) {
    99  	var q0PlusQ common.Poly
   100  
   101  	// See useHint() and makeHint() for an explanation.  We reimplement it
   102  	// here so that we can call Poly.Decompose(), which might be way faster
   103  	// than calling decompose() in a loop (for instance when having AVX2.)
   104  
   105  	PolyDecompose(q, &q0PlusQ, p)
   106  
   107  	for i := 0; i < common.N; i++ {
   108  		if hint[i] == 0 {
   109  			continue
   110  		}
   111  		if Gamma2 == 261888 {
   112  			if q0PlusQ[i] > common.Q {
   113  				p[i] = (p[i] + 1) & 15
   114  			} else {
   115  				p[i] = (p[i] - 1) & 15
   116  			}
   117  		} else if Gamma2 == 95232 {
   118  			if q0PlusQ[i] > common.Q {
   119  				if p[i] == 43 {
   120  					p[i] = 0
   121  				} else {
   122  					p[i]++
   123  				}
   124  			} else {
   125  				if p[i] == 0 {
   126  					p[i] = 43
   127  				} else {
   128  					p[i]--
   129  				}
   130  			}
   131  		} else {
   132  			panic("unsupported γ₂")
   133  		}
   134  	}
   135  }
   136  
   137  // Splits each of the coefficients of p using decompose.
   138  func PolyDecompose(p, p0PlusQ, p1 *common.Poly) {
   139  	for i := 0; i < common.N; i++ {
   140  		p0PlusQ[i], p1[i] = decompose(p[i])
   141  	}
   142  }