/**
 * Copyright 2021 Cloudflare Inc
 *
 * 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.
 */ export function verifyPosRange ( a , n ) { if ( ! ( BigInt ( 0 ) <= a && a < n ) ) { throw new Error ( 'a not in range' ) ; } return true ; } export function bitLen ( n ) { return n . toString ( 2 ) . length ; } export function byteLen ( n ) { return Math . ceil ( bitLen ( n ) / 8 ) ; } export function isOdd ( n ) { return n % BigInt ( 2 ) === BigInt ( 1 ) ; } export function isEven ( n ) { return n % BigInt ( 2 ) === BigInt ( 0 ) ; } export function posMod ( n , p ) { let r = n % p ; if ( r < BigInt ( 0 ) ) { r = ( r + p ) % p ; } return r ; } export function expMod ( n , e , p ) { if ( e < BigInt ( 0 ) ) { throw new Error ( 'neg expo' ) ; } let r = BigInt ( 1 ) , q = n , k = e ; while ( k > BigInt ( 0 ) ) { if ( isOdd ( k ) ) { r = ( r * q ) % p ; } q = ( q * q ) % p ; k = k / BigInt ( 2 ) ; } return r ; } export function isNonNegative ( n , p ) { const pMinus1div2 = ( p - BigInt ( 1 ) ) >> BigInt ( 1 ) ; return 0 <= n && n <= pMinus1div2 ; } export function absolute ( n , p ) { return isNonNegative ( n , p ) ? n : posMod ( - n , p ) ; } export function isSquare ( n , p ) { const q = ( p - BigInt ( 1 ) ) >> BigInt ( 1 ) ; return expMod ( n , q , p ) === BigInt ( 1 ) ; } export function invSqrtMod ( n , p ) { const q = ( p + BigInt ( 1 ) ) >> BigInt ( 2 ) ; return expMod ( invEuclid ( n , p ) , q , p ) ; } export function invMod ( n , p ) { return invEuclid ( n , p ) ; } function extendedEuclid ( X , Y ) { let a = BigInt ( 1 ) , b = BigInt ( 0 ) , c = BigInt ( 0 ) , d = BigInt ( 1 ) , x = X , y = Y , t = BigInt ( 0 ) ; while ( y != BigInt ( 0 ) ) { const q = x / y ; a = a - c * q ; b = b - d * q ; x = x - q * y ; t = x ; x = y ; y = t ; t = a ; a = c ; c = t ; t = b ; b = d ; d = t ; } return { g : x , a : a , b : b , } ; } export function invEuclid ( t , N ) { const res = extendedEuclid ( t , N ) ; let inv = res . a ; if ( inv < 0 ) { inv += N ; } return inv ; } export function toBytes ( n , len ) { const maxBig = BigInt ( 1 ) << ( BigInt ( 8 ) * BigInt ( len ) ) ; if ( ! ( len > 0 && BigInt ( 0 ) <= n && n < maxBig ) ) { throw new Error ( "number doesn't fit in array" ) ; } const ret = new Uint8Array ( len ) ; let t = n ; for ( let i = 0 ; i < len ; i ++ ) { const b = t & BigInt ( 255 ) ; ret [ len - 1 - i ] = parseInt ( b . toString ( ) ) ; t >>= BigInt ( 8 ) ; } return ret ; } export async function hashNums ( nums ) { const numNums = nums . length , data = [ ] , enc = new TextEncoder ( ) ; let totBytes = 0 ; for ( let i = 0 ; i < numNums ; i ++ ) { const numBytes = enc . encode ( nums [ i ] . toString ( ) ) ; totBytes += numBytes . byteLength ; data . push ( numBytes ) ; } const tbh = new Uint8Array ( totBytes + 4 * numNums ) ; let index = 0 ; for ( let i = 0 ; i < numNums ; i ++ ) { tbh [ index ++ ] = ( data [ i ] . byteLength >> 24 ) & 0xff ; tbh [ index ++ ] = ( data [ i ] . byteLength >> 16 ) & 0xff ; tbh [ index ++ ] = ( data [ i ] . byteLength >> 8 ) & 0xff ; tbh [ index ++ ] = data [ i ] . byteLength & 0xff ; tbh . set ( data [ i ] , index ) ; index += data [ i ] . byteLength ; } const hash = await crypto . subtle . digest ( 'SHA-256' , tbh ) ; return fromBytes ( new Uint8Array ( hash . slice ( 0 , 10 ) ) ) ; } export function fromBytes ( a ) { let k = BigInt ( 0 ) ; for ( const ai of a ) { k <<= BigInt ( 8 ) ; k |= BigInt ( ai ) ; } return k ; } export function rnd ( n ) { const buffer = new Uint8Array ( byteLen ( n ) ) ; while ( true ) { crypto . getRandomValues ( buffer ) ; const ret = fromBytes ( buffer ) ; if ( ret < n ) { return ret ; } } } export function rndRange ( min , max ) { return rnd ( max - min + BigInt ( 1 ) ) + min ; } export function isPrime ( n , iterations = 7 ) { if ( n === BigInt ( 2 ) || n === BigInt ( 3 ) ) { return true ; } if ( isEven ( n ) || n < BigInt ( 2 ) ) { return false ; } const nminusone = n - BigInt ( 1 ) ; let s = 0 , d = nminusone ; while ( isEven ( d ) ) { d >>= BigInt ( 1 ) ; s ++ ; } let k = iterations ; WitnessLoop : do { const base = rnd ( n - BigInt ( 3 ) ) + BigInt ( 2 ) ; let x = expMod ( base , d , n ) ; if ( x === BigInt ( 1 ) || x === n - BigInt ( 1 ) ) { continue ; } for ( let i = s - 1 ; i >= 0 ; i -- ) { x = x ** BigInt ( 2 ) % n ; if ( x === BigInt ( 1 ) ) { return false ; } if ( x === n - BigInt ( 1 ) ) { continue WitnessLoop ; } } return false ; } while ( ( k -= 1 ) ) ; return true ; } export const serdeBigInt = { isRequired : true , serializer : function ( v ) : string { let s = '0x' ; if ( v < BigInt ( 0 ) ) { v = - v ; s = '-' + s ; } return s + v . toString ( 16 ) ; } , deserializer : function ( this , v ) : bigint { if ( ! v ) { throw new Error ( 'the field ' + this . key + ' is required' ) ; } if ( v . charAt ( 0 ) === '-' ) { return - BigInt ( v . slice ( 1 ) ) ; } return BigInt ( v ) ; } , } ;