// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!

import { UniFFITypeError } from "resource://gre/modules/UniFFI.sys.mjs";



// Objects intended to be used in the unit tests
export var UnitTestObjs = {};

// Write/Read data to/from an ArrayBuffer
class ArrayBufferDataStream {
    constructor(arrayBuffer) {
        this.dataView = new DataView(arrayBuffer);
        this.pos = 0;
    }

    readUint8() {
        let rv = this.dataView.getUint8(this.pos);
        this.pos += 1;
        return rv;
    }

    writeUint8(value) {
        this.dataView.setUint8(this.pos, value);
        this.pos += 1;
    }

    readUint16() {
        let rv = this.dataView.getUint16(this.pos);
        this.pos += 2;
        return rv;
    }

    writeUint16(value) {
        this.dataView.setUint16(this.pos, value);
        this.pos += 2;
    }

    readUint32() {
        let rv = this.dataView.getUint32(this.pos);
        this.pos += 4;
        return rv;
    }

    writeUint32(value) {
        this.dataView.setUint32(this.pos, value);
        this.pos += 4;
    }

    readUint64() {
        let rv = this.dataView.getBigUint64(this.pos);
        this.pos += 8;
        return Number(rv);
    }

    writeUint64(value) {
        this.dataView.setBigUint64(this.pos, BigInt(value));
        this.pos += 8;
    }


    readInt8() {
        let rv = this.dataView.getInt8(this.pos);
        this.pos += 1;
        return rv;
    }

    writeInt8(value) {
        this.dataView.setInt8(this.pos, value);
        this.pos += 1;
    }

    readInt16() {
        let rv = this.dataView.getInt16(this.pos);
        this.pos += 2;
        return rv;
    }

    writeInt16(value) {
        this.dataView.setInt16(this.pos, value);
        this.pos += 2;
    }

    readInt32() {
        let rv = this.dataView.getInt32(this.pos);
        this.pos += 4;
        return rv;
    }

    writeInt32(value) {
        this.dataView.setInt32(this.pos, value);
        this.pos += 4;
    }

    readInt64() {
        let rv = this.dataView.getBigInt64(this.pos);
        this.pos += 8;
        return Number(rv);
    }

    writeInt64(value) {
        this.dataView.setBigInt64(this.pos, BigInt(value));
        this.pos += 8;
    }

    readFloat32() {
        let rv = this.dataView.getFloat32(this.pos);
        this.pos += 4;
        return rv;
    }

    writeFloat32(value) {
        this.dataView.setFloat32(this.pos, value);
        this.pos += 4;
    }

    readFloat64() {
        let rv = this.dataView.getFloat64(this.pos);
        this.pos += 8;
        return rv;
    }

    writeFloat64(value) {
        this.dataView.setFloat64(this.pos, value);
        this.pos += 8;
    }


    writeString(value) {
      const encoder = new TextEncoder();
      // Note: in order to efficiently write this data, we first write the
      // string data, reserving 4 bytes for the size.
      const dest = new Uint8Array(this.dataView.buffer, this.pos + 4);
      const encodeResult = encoder.encodeInto(value, dest);
      if (encodeResult.read != value.length) {
        throw new UniFFIError(
            "writeString: out of space when writing to ArrayBuffer.  Did the computeSize() method returned the wrong result?"
        );
      }
      const size = encodeResult.written;
      // Next, go back and write the size before the string data
      this.dataView.setUint32(this.pos, size);
      // Finally, advance our position past both the size and string data
      this.pos += size + 4;
    }

    readString() {
      const decoder = new TextDecoder();
      const size = this.readUint32();
      const source = new Uint8Array(this.dataView.buffer, this.pos, size)
      const value = decoder.decode(source);
      this.pos += size;
      return value;
    }

    readBytes() {
      const size = this.readInt32();
      const bytes = new Uint8Array(this.dataView.buffer, this.pos, size);
      this.pos += size;
      return bytes
    }

    writeBytes(uint8Array) {
      this.writeUint32(uint8Array.length);
      value.forEach((elt) => {
        dataStream.writeUint8(elt);
      })
    }

    // Reads a RelevancyStore pointer from the data stream
    // UniFFI Pointers are **always** 8 bytes long. That is enforced
    // by the C++ and Rust Scaffolding code.
    readPointerRelevancyStore() {
        const pointerId = 0; // relevancy:RelevancyStore
        const res = UniFFIScaffolding.readPointer(pointerId, this.dataView.buffer, this.pos);
        this.pos += 8;
        return res;
    }

    // Writes a RelevancyStore pointer into the data stream
    // UniFFI Pointers are **always** 8 bytes long. That is enforced
    // by the C++ and Rust Scaffolding code.
    writePointerRelevancyStore(value) {
        const pointerId = 0; // relevancy:RelevancyStore
        UniFFIScaffolding.writePointer(pointerId, value, this.dataView.buffer, this.pos);
        this.pos += 8;
    }
    
}

function handleRustResult(result, liftCallback, liftErrCallback) {
    switch (result.code) {
        case "success":
            return liftCallback(result.data);

        case "error":
            throw liftErrCallback(result.data);

        case "internal-error":
            if (result.data) {
                throw new UniFFIInternalError(FfiConverterString.lift(result.data));
            } else {
                throw new UniFFIInternalError("Unknown error");
            }

        default:
            throw new UniFFIError(`Unexpected status code: ${result.code}`);
    }
}

class UniFFIError {
    constructor(message) {
        this.message = message;
    }

    toString() {
        return `UniFFIError: ${this.message}`
    }
}

class UniFFIInternalError extends UniFFIError {}

// Base class for FFI converters
class FfiConverter {
    // throw `UniFFITypeError` if a value to be converted has an invalid type
    static checkType(value) {
        if (value === undefined ) {
            throw new UniFFITypeError(`undefined`);
        }
        if (value === null ) {
            throw new UniFFITypeError(`null`);
        }
    }
}

// Base class for FFI converters that lift/lower by reading/writing to an ArrayBuffer
class FfiConverterArrayBuffer extends FfiConverter {
    static lift(buf) {
        return this.read(new ArrayBufferDataStream(buf));
    }

    static lower(value) {
        const buf = new ArrayBuffer(this.computeSize(value));
        const dataStream = new ArrayBufferDataStream(buf);
        this.write(dataStream, value);
        return buf;
    }
}

// Symbols that are used to ensure that Object constructors
// can only be used with a proper UniFFI pointer
const uniffiObjectPtr = Symbol("uniffiObjectPtr");
const constructUniffiObject = Symbol("constructUniffiObject");
UnitTestObjs.uniffiObjectPtr = uniffiObjectPtr;

// Export the FFIConverter object to make external types work.
export class FfiConverterU32 extends FfiConverter {
    static checkType(value) {
        super.checkType(value);
        if (!Number.isInteger(value)) {
            throw new UniFFITypeError(`${value} is not an integer`);
        }
        if (value < 0 || value > 4294967295) {
            throw new UniFFITypeError(`${value} exceeds the U32 bounds`);
        }
    }
    static computeSize() {
        return 4;
    }
    static lift(value) {
        return value;
    }
    static lower(value) {
        return value;
    }
    static write(dataStream, value) {
        dataStream.writeUint32(value)
    }
    static read(dataStream) {
        return dataStream.readUint32()
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterU64 extends FfiConverter {
    static checkType(value) {
        super.checkType(value);
        if (!Number.isSafeInteger(value)) {
            throw new UniFFITypeError(`${value} exceeds the safe integer bounds`);
        }
        if (value < 0) {
            throw new UniFFITypeError(`${value} exceeds the U64 bounds`);
        }
    }
    static computeSize() {
        return 8;
    }
    static lift(value) {
        return value;
    }
    static lower(value) {
        return value;
    }
    static write(dataStream, value) {
        dataStream.writeUint64(value)
    }
    static read(dataStream) {
        return dataStream.readUint64()
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterF64 extends FfiConverter {
    static computeSize() {
        return 8;
    }
    static lift(value) {
        return value;
    }
    static lower(value) {
        return value;
    }
    static write(dataStream, value) {
        dataStream.writeFloat64(value)
    }
    static read(dataStream) {
        return dataStream.readFloat64()
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterBool extends FfiConverter {
    static computeSize() {
        return 1;
    }
    static lift(value) {
        return value == 1;
    }
    static lower(value) {
        if (value) {
            return 1;
        } else {
            return 0;
        }
    }
    static write(dataStream, value) {
        dataStream.writeUint8(this.lower(value))
    }
    static read(dataStream) {
        return this.lift(dataStream.readUint8())
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterString extends FfiConverter {
    static checkType(value) {
        super.checkType(value);
        if (typeof value !== "string") {
            throw new UniFFITypeError(`${value} is not a string`);
        }
    }

    static lift(buf) {
        const decoder = new TextDecoder();
        const utf8Arr = new Uint8Array(buf);
        return decoder.decode(utf8Arr);
    }
    static lower(value) {
        const encoder = new TextEncoder();
        return encoder.encode(value).buffer;
    }

    static write(dataStream, value) {
        dataStream.writeString(value);
    }

    static read(dataStream) {
        return dataStream.readString();
    }

    static computeSize(value) {
        const encoder = new TextEncoder();
        return 4 + encoder.encode(value).length
    }
}

/**
 * RelevancyStore
 */
export class RelevancyStore {
    // Use `init` to instantiate this class.
    // DO NOT USE THIS CONSTRUCTOR DIRECTLY
    constructor(opts) {
        if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
            throw new UniFFIError("Attempting to construct an object using the JavaScript constructor directly" +
            "Please use a UDL defined constructor, or the init function for the primary constructor")
        }
        if (!opts[constructUniffiObject] instanceof UniFFIPointer) {
            throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
        }
        this[uniffiObjectPtr] = opts[constructUniffiObject];
    }
    /**
     * Construct a new RelevancyStore
     *
     * This is non-blocking since databases and other resources are lazily opened.
     * @returns {RelevancyStore}
     */
    static init(dbPath) {
        const liftResult = (result) => FfiConverterTypeRelevancyStore.lift(result);
        const liftError = null;
        const functionCall = () => {
            try {
                FfiConverterString.checkType(dbPath)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("dbPath");
                }
                throw e;
            }
            return UniFFIScaffolding.callSync(
                12, // relevancy:uniffi_relevancy_fn_constructor_relevancystore_new
                FfiConverterString.lower(dbPath),
            )
        }
        return handleRustResult(functionCall(), liftResult, liftError);}

    /**
     * Initializes probability distributions for any uninitialized items (arms) within a bandit model.
     *
     * This method takes a `bandit` identifier and a list of `arms` (items) and ensures that each arm
     * in the list has an initialized probability distribution in the database. For each arm, if the
     * probability distribution does not already exist, it will be created, using Beta(1,1) as default,
     * which represents uniform distribution.
     */
    banditInit(bandit,arms) {
        const liftResult = (result) => undefined;
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            try {
                FfiConverterString.checkType(bandit)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("bandit");
                }
                throw e;
            }
            try {
                FfiConverterSequencestring.checkType(arms)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("arms");
                }
                throw e;
            }
            return UniFFIScaffolding.callAsyncWrapper(
                3, // relevancy:uniffi_relevancy_fn_method_relevancystore_bandit_init
                FfiConverterTypeRelevancyStore.lower(this),
                FfiConverterString.lower(bandit),
                FfiConverterSequencestring.lower(arms),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Selects the optimal item (arm) to display to the user based on a multi-armed bandit model.
     *
     * This method takes in a `bandit` identifier and a list of possible `arms` (items) and uses a
     * Thompson sampling approach to select the arm with the highest probability of success.
     * For each arm, it retrieves the Beta distribution parameters (alpha and beta) from the
     * database, creates a Beta distribution, and samples from it to estimate the arm's probability
     * of success. The arm with the highest sampled probability is selected and returned.
     * @returns {string}
     */
    banditSelect(bandit,arms) {
        const liftResult = (result) => FfiConverterString.lift(result);
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            try {
                FfiConverterString.checkType(bandit)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("bandit");
                }
                throw e;
            }
            try {
                FfiConverterSequencestring.checkType(arms)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("arms");
                }
                throw e;
            }
            return UniFFIScaffolding.callAsyncWrapper(
                4, // relevancy:uniffi_relevancy_fn_method_relevancystore_bandit_select
                FfiConverterTypeRelevancyStore.lower(this),
                FfiConverterString.lower(bandit),
                FfiConverterSequencestring.lower(arms),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Updates the bandit model's arm data based on user interaction (selection or non-selection).
     *
     * This method takes in a `bandit` identifier, an `arm` identifier, and a `selected` flag.
     * If `selected` is true, it updates the model to reflect a successful selection of the arm,
     * reinforcing its positive reward probability. If `selected` is false, it updates the
     * beta (failure) distribution of the arm, reflecting a lack of selection and reinforcing
     * its likelihood of a negative outcome.
     */
    banditUpdate(bandit,arm,selected) {
        const liftResult = (result) => undefined;
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            try {
                FfiConverterString.checkType(bandit)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("bandit");
                }
                throw e;
            }
            try {
                FfiConverterString.checkType(arm)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("arm");
                }
                throw e;
            }
            try {
                FfiConverterBool.checkType(selected)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("selected");
                }
                throw e;
            }
            return UniFFIScaffolding.callAsyncWrapper(
                5, // relevancy:uniffi_relevancy_fn_method_relevancystore_bandit_update
                FfiConverterTypeRelevancyStore.lower(this),
                FfiConverterString.lower(bandit),
                FfiConverterString.lower(arm),
                FfiConverterBool.lower(selected),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Calculate metrics for the validation phase
     *
     * This runs after [Self::ingest].  It takes the interest vector that ingest created and
     * calculates a set of metrics that we can report to glean.
     * @returns {InterestMetrics}
     */
    calculateMetrics() {
        const liftResult = (result) => FfiConverterTypeInterestMetrics.lift(result);
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            return UniFFIScaffolding.callAsyncWrapper(
                6, // relevancy:uniffi_relevancy_fn_method_relevancystore_calculate_metrics
                FfiConverterTypeRelevancyStore.lower(this),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Close any open resources (for example databases)
     *
     * Calling `close` will interrupt any in-progress queries on other threads.
     */
    close() {
        const liftResult = (result) => undefined;
        const liftError = null;
        const functionCall = () => {
            return UniFFIScaffolding.callSync(
                7, // relevancy:uniffi_relevancy_fn_method_relevancystore_close
                FfiConverterTypeRelevancyStore.lower(this),
            )
        }
        return handleRustResult(functionCall(), liftResult, liftError);
    }

    /**
     * Retrieves the data for a specific bandit and arm.
     * @returns {BanditData}
     */
    getBanditData(bandit,arm) {
        const liftResult = (result) => FfiConverterTypeBanditData.lift(result);
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            try {
                FfiConverterString.checkType(bandit)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("bandit");
                }
                throw e;
            }
            try {
                FfiConverterString.checkType(arm)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("arm");
                }
                throw e;
            }
            return UniFFIScaffolding.callAsyncWrapper(
                8, // relevancy:uniffi_relevancy_fn_method_relevancystore_get_bandit_data
                FfiConverterTypeRelevancyStore.lower(this),
                FfiConverterString.lower(bandit),
                FfiConverterString.lower(arm),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Ingest top URLs to build the user's interest vector.
     *
     * Consumer should pass a list of the user's top URLs by frecency to this method.  It will
     * then:
     *
     * - Download the URL interest data from remote settings.  Eventually this should be cached /
     * stored in the database, but for now it would be fine to download fresh data each time.
     * - Match the user's top URls against the interest data to build up their interest vector.
     * - Store the user's interest vector in the database.
     *
     * This method may execute for a long time and should only be called from a worker thread.
     * @returns {InterestVector}
     */
    ingest(topUrlsByFrecency) {
        const liftResult = (result) => FfiConverterTypeInterestVector.lift(result);
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            try {
                FfiConverterSequencestring.checkType(topUrlsByFrecency)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("topUrlsByFrecency");
                }
                throw e;
            }
            return UniFFIScaffolding.callAsyncWrapper(
                9, // relevancy:uniffi_relevancy_fn_method_relevancystore_ingest
                FfiConverterTypeRelevancyStore.lower(this),
                FfiConverterSequencestring.lower(topUrlsByFrecency),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

    /**
     * Interrupt any current database queries
     */
    interrupt() {
        const liftResult = (result) => undefined;
        const liftError = null;
        const functionCall = () => {
            return UniFFIScaffolding.callSync(
                10, // relevancy:uniffi_relevancy_fn_method_relevancystore_interrupt
                FfiConverterTypeRelevancyStore.lower(this),
            )
        }
        return handleRustResult(functionCall(), liftResult, liftError);
    }

    /**
     * Get the user's interest vector directly.
     *
     * This runs after [Self::ingest].  It returns the interest vector directly so that the
     * consumer can show it in an `about:` page.
     * @returns {InterestVector}
     */
    userInterestVector() {
        const liftResult = (result) => FfiConverterTypeInterestVector.lift(result);
        const liftError = (data) => FfiConverterTypeRelevancyApiError.lift(data);
        const functionCall = () => {
            return UniFFIScaffolding.callAsyncWrapper(
                11, // relevancy:uniffi_relevancy_fn_method_relevancystore_user_interest_vector
                FfiConverterTypeRelevancyStore.lower(this),
            )
        }
        try {
            return functionCall().then((result) => handleRustResult(result, liftResult, liftError));
        }  catch (error) {
            return Promise.reject(error)
        }
    }

}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRelevancyStore extends FfiConverter {
    static lift(value) {
        const opts = {};
        opts[constructUniffiObject] = value;
        return new RelevancyStore(opts);
    }

    static lower(value) {
        const ptr = value[uniffiObjectPtr];
        if (!(ptr instanceof UniFFIPointer)) {
            throw new UniFFITypeError("Object is not a 'RelevancyStore' instance");
        }
        return ptr;
    }

    static read(dataStream) {
        return this.lift(dataStream.readPointerRelevancyStore());
    }

    static write(dataStream, value) {
        dataStream.writePointerRelevancyStore(value[uniffiObjectPtr]);
    }

    static computeSize(value) {
        return 8;
    }
}

/**
 * BanditData
 */
export class BanditData {
    constructor({ bandit, arm, impressions, clicks, alpha, beta } = {}) {
        try {
            FfiConverterString.checkType(bandit)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("bandit");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(arm)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("arm");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(impressions)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("impressions");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(clicks)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("clicks");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(alpha)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("alpha");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(beta)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("beta");
            }
            throw e;
        }
        /**
         * @type {string}
         */
        this.bandit = bandit;
        /**
         * @type {string}
         */
        this.arm = arm;
        /**
         * @type {number}
         */
        this.impressions = impressions;
        /**
         * @type {number}
         */
        this.clicks = clicks;
        /**
         * @type {number}
         */
        this.alpha = alpha;
        /**
         * @type {number}
         */
        this.beta = beta;
    }

    equals(other) {
        return (
            this.bandit == other.bandit &&
            this.arm == other.arm &&
            this.impressions == other.impressions &&
            this.clicks == other.clicks &&
            this.alpha == other.alpha &&
            this.beta == other.beta
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeBanditData extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new BanditData({
            bandit: FfiConverterString.read(dataStream),
            arm: FfiConverterString.read(dataStream),
            impressions: FfiConverterU64.read(dataStream),
            clicks: FfiConverterU64.read(dataStream),
            alpha: FfiConverterU64.read(dataStream),
            beta: FfiConverterU64.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value.bandit);
        FfiConverterString.write(dataStream, value.arm);
        FfiConverterU64.write(dataStream, value.impressions);
        FfiConverterU64.write(dataStream, value.clicks);
        FfiConverterU64.write(dataStream, value.alpha);
        FfiConverterU64.write(dataStream, value.beta);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterString.computeSize(value.bandit);
        totalSize += FfiConverterString.computeSize(value.arm);
        totalSize += FfiConverterU64.computeSize(value.impressions);
        totalSize += FfiConverterU64.computeSize(value.clicks);
        totalSize += FfiConverterU64.computeSize(value.alpha);
        totalSize += FfiConverterU64.computeSize(value.beta);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof BanditData)) {
            throw new UniFFITypeError(`Expected 'BanditData', found '${typeof value}'`);
        }
        try {
            FfiConverterString.checkType(value.bandit);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".bandit");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(value.arm);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".arm");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(value.impressions);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".impressions");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(value.clicks);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".clicks");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(value.alpha);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".alpha");
            }
            throw e;
        }
        try {
            FfiConverterU64.checkType(value.beta);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".beta");
            }
            throw e;
        }
    }
}

/**
 * Interest metrics that we want to send to Glean as part of the validation process.  These contain
 * the cosine similarity when comparing the user's interest against various interest vectors that
 * consumers may use.
 *
 * Cosine similarly was chosen because it seems easy to calculate.  This was then matched against
 * some semi-plausible real-world interest vectors that consumers might use.  This is all up for
 * debate and we may decide to switch to some other metrics.
 *
 * Similarity values are transformed to integers by multiplying the floating point value by 1000 and
 * rounding.  This is to make them compatible with Glean's distribution metrics.
 */
export class InterestMetrics {
    constructor({ topSingleInterestSimilarity, top2interestSimilarity, top3interestSimilarity } = {}) {
        try {
            FfiConverterU32.checkType(topSingleInterestSimilarity)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("topSingleInterestSimilarity");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(top2interestSimilarity)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("top2interestSimilarity");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(top3interestSimilarity)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("top3interestSimilarity");
            }
            throw e;
        }
        /**
         * Similarity between the user's interest vector and an interest vector where the element for
         * the user's top interest is copied, but all other interests are set to zero.  This measures
         * the highest possible similarity with consumers that used interest vectors with a single
         * interest set.
         * @type {number}
         */
        this.topSingleInterestSimilarity = topSingleInterestSimilarity;
        /**
         * The same as before, but the top 2 interests are copied. This measures the highest possible
         * similarity with consumers that used interest vectors with a two interests (note: this means
         * they would need to choose the user's top two interests and have the exact same proportion
         * between them as the user).
         * @type {number}
         */
        this.top2interestSimilarity = top2interestSimilarity;
        /**
         * The same as before, but the top 3 interests are copied.
         * @type {number}
         */
        this.top3interestSimilarity = top3interestSimilarity;
    }

    equals(other) {
        return (
            this.topSingleInterestSimilarity == other.topSingleInterestSimilarity &&
            this.top2interestSimilarity == other.top2interestSimilarity &&
            this.top3interestSimilarity == other.top3interestSimilarity
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeInterestMetrics extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new InterestMetrics({
            topSingleInterestSimilarity: FfiConverterU32.read(dataStream),
            top2interestSimilarity: FfiConverterU32.read(dataStream),
            top3interestSimilarity: FfiConverterU32.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterU32.write(dataStream, value.topSingleInterestSimilarity);
        FfiConverterU32.write(dataStream, value.top2interestSimilarity);
        FfiConverterU32.write(dataStream, value.top3interestSimilarity);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterU32.computeSize(value.topSingleInterestSimilarity);
        totalSize += FfiConverterU32.computeSize(value.top2interestSimilarity);
        totalSize += FfiConverterU32.computeSize(value.top3interestSimilarity);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof InterestMetrics)) {
            throw new UniFFITypeError(`Expected 'InterestMetrics', found '${typeof value}'`);
        }
        try {
            FfiConverterU32.checkType(value.topSingleInterestSimilarity);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".topSingleInterestSimilarity");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.top2interestSimilarity);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".top2interestSimilarity");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.top3interestSimilarity);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".top3interestSimilarity");
            }
            throw e;
        }
    }
}

/**
 * Vector storing a count value for each interest
 *
 * Here "vector" refers to the mathematical object, not a Rust `Vec`.  It always has a fixed
 * number of elements.
 */
export class InterestVector {
    constructor({ inconclusive, animals, arts, autos, business, career, education, fashion, finance, food, government, hobbies, home, news, realEstate, society, sports, tech, travel } = {}) {
        try {
            FfiConverterU32.checkType(inconclusive)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("inconclusive");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(animals)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("animals");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(arts)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("arts");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(autos)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("autos");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(business)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("business");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(career)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("career");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(education)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("education");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(fashion)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("fashion");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(finance)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("finance");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(food)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("food");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(government)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("government");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(hobbies)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("hobbies");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(home)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("home");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(news)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("news");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(realEstate)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("realEstate");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(society)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("society");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(sports)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("sports");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(tech)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("tech");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(travel)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("travel");
            }
            throw e;
        }
        /**
         * @type {number}
         */
        this.inconclusive = inconclusive;
        /**
         * @type {number}
         */
        this.animals = animals;
        /**
         * @type {number}
         */
        this.arts = arts;
        /**
         * @type {number}
         */
        this.autos = autos;
        /**
         * @type {number}
         */
        this.business = business;
        /**
         * @type {number}
         */
        this.career = career;
        /**
         * @type {number}
         */
        this.education = education;
        /**
         * @type {number}
         */
        this.fashion = fashion;
        /**
         * @type {number}
         */
        this.finance = finance;
        /**
         * @type {number}
         */
        this.food = food;
        /**
         * @type {number}
         */
        this.government = government;
        /**
         * @type {number}
         */
        this.hobbies = hobbies;
        /**
         * @type {number}
         */
        this.home = home;
        /**
         * @type {number}
         */
        this.news = news;
        /**
         * @type {number}
         */
        this.realEstate = realEstate;
        /**
         * @type {number}
         */
        this.society = society;
        /**
         * @type {number}
         */
        this.sports = sports;
        /**
         * @type {number}
         */
        this.tech = tech;
        /**
         * @type {number}
         */
        this.travel = travel;
    }

    equals(other) {
        return (
            this.inconclusive == other.inconclusive &&
            this.animals == other.animals &&
            this.arts == other.arts &&
            this.autos == other.autos &&
            this.business == other.business &&
            this.career == other.career &&
            this.education == other.education &&
            this.fashion == other.fashion &&
            this.finance == other.finance &&
            this.food == other.food &&
            this.government == other.government &&
            this.hobbies == other.hobbies &&
            this.home == other.home &&
            this.news == other.news &&
            this.realEstate == other.realEstate &&
            this.society == other.society &&
            this.sports == other.sports &&
            this.tech == other.tech &&
            this.travel == other.travel
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeInterestVector extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new InterestVector({
            inconclusive: FfiConverterU32.read(dataStream),
            animals: FfiConverterU32.read(dataStream),
            arts: FfiConverterU32.read(dataStream),
            autos: FfiConverterU32.read(dataStream),
            business: FfiConverterU32.read(dataStream),
            career: FfiConverterU32.read(dataStream),
            education: FfiConverterU32.read(dataStream),
            fashion: FfiConverterU32.read(dataStream),
            finance: FfiConverterU32.read(dataStream),
            food: FfiConverterU32.read(dataStream),
            government: FfiConverterU32.read(dataStream),
            hobbies: FfiConverterU32.read(dataStream),
            home: FfiConverterU32.read(dataStream),
            news: FfiConverterU32.read(dataStream),
            realEstate: FfiConverterU32.read(dataStream),
            society: FfiConverterU32.read(dataStream),
            sports: FfiConverterU32.read(dataStream),
            tech: FfiConverterU32.read(dataStream),
            travel: FfiConverterU32.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterU32.write(dataStream, value.inconclusive);
        FfiConverterU32.write(dataStream, value.animals);
        FfiConverterU32.write(dataStream, value.arts);
        FfiConverterU32.write(dataStream, value.autos);
        FfiConverterU32.write(dataStream, value.business);
        FfiConverterU32.write(dataStream, value.career);
        FfiConverterU32.write(dataStream, value.education);
        FfiConverterU32.write(dataStream, value.fashion);
        FfiConverterU32.write(dataStream, value.finance);
        FfiConverterU32.write(dataStream, value.food);
        FfiConverterU32.write(dataStream, value.government);
        FfiConverterU32.write(dataStream, value.hobbies);
        FfiConverterU32.write(dataStream, value.home);
        FfiConverterU32.write(dataStream, value.news);
        FfiConverterU32.write(dataStream, value.realEstate);
        FfiConverterU32.write(dataStream, value.society);
        FfiConverterU32.write(dataStream, value.sports);
        FfiConverterU32.write(dataStream, value.tech);
        FfiConverterU32.write(dataStream, value.travel);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterU32.computeSize(value.inconclusive);
        totalSize += FfiConverterU32.computeSize(value.animals);
        totalSize += FfiConverterU32.computeSize(value.arts);
        totalSize += FfiConverterU32.computeSize(value.autos);
        totalSize += FfiConverterU32.computeSize(value.business);
        totalSize += FfiConverterU32.computeSize(value.career);
        totalSize += FfiConverterU32.computeSize(value.education);
        totalSize += FfiConverterU32.computeSize(value.fashion);
        totalSize += FfiConverterU32.computeSize(value.finance);
        totalSize += FfiConverterU32.computeSize(value.food);
        totalSize += FfiConverterU32.computeSize(value.government);
        totalSize += FfiConverterU32.computeSize(value.hobbies);
        totalSize += FfiConverterU32.computeSize(value.home);
        totalSize += FfiConverterU32.computeSize(value.news);
        totalSize += FfiConverterU32.computeSize(value.realEstate);
        totalSize += FfiConverterU32.computeSize(value.society);
        totalSize += FfiConverterU32.computeSize(value.sports);
        totalSize += FfiConverterU32.computeSize(value.tech);
        totalSize += FfiConverterU32.computeSize(value.travel);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof InterestVector)) {
            throw new UniFFITypeError(`Expected 'InterestVector', found '${typeof value}'`);
        }
        try {
            FfiConverterU32.checkType(value.inconclusive);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".inconclusive");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.animals);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".animals");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.arts);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".arts");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.autos);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".autos");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.business);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".business");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.career);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".career");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.education);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".education");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.fashion);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".fashion");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.finance);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".finance");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.food);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".food");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.government);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".government");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.hobbies);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".hobbies");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.home);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".home");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.news);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".news");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.realEstate);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".realEstate");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.society);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".society");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.sports);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".sports");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.tech);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".tech");
            }
            throw e;
        }
        try {
            FfiConverterU32.checkType(value.travel);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".travel");
            }
            throw e;
        }
    }
}


/**
 * List of possible interests for a domain.  Domains can have be associated with one or multiple
 * interests.  `Inconclusive` is used for domains in the user's top sites that we can't classify
 * because there's no corresponding entry in the interest database.
 */
export const Interest = {
    /**
     * INCONCLUSIVE
     */
    INCONCLUSIVE: 1,
    /**
     * ANIMALS
     */
    ANIMALS: 2,
    /**
     * ARTS
     */
    ARTS: 3,
    /**
     * AUTOS
     */
    AUTOS: 4,
    /**
     * BUSINESS
     */
    BUSINESS: 5,
    /**
     * CAREER
     */
    CAREER: 6,
    /**
     * EDUCATION
     */
    EDUCATION: 7,
    /**
     * FASHION
     */
    FASHION: 8,
    /**
     * FINANCE
     */
    FINANCE: 9,
    /**
     * FOOD
     */
    FOOD: 10,
    /**
     * GOVERNMENT
     */
    GOVERNMENT: 11,
    /**
     * HOBBIES
     */
    HOBBIES: 12,
    /**
     * HOME
     */
    HOME: 13,
    /**
     * NEWS
     */
    NEWS: 14,
    /**
     * REAL_ESTATE
     */
    REAL_ESTATE: 15,
    /**
     * SOCIETY
     */
    SOCIETY: 16,
    /**
     * SPORTS
     */
    SPORTS: 17,
    /**
     * TECH
     */
    TECH: 18,
    /**
     * TRAVEL
     */
    TRAVEL: 19,
};

Object.freeze(Interest);
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeInterest extends FfiConverterArrayBuffer {
    static read(dataStream) {
        switch (dataStream.readInt32()) {
            case 1:
                return Interest.INCONCLUSIVE
            case 2:
                return Interest.ANIMALS
            case 3:
                return Interest.ARTS
            case 4:
                return Interest.AUTOS
            case 5:
                return Interest.BUSINESS
            case 6:
                return Interest.CAREER
            case 7:
                return Interest.EDUCATION
            case 8:
                return Interest.FASHION
            case 9:
                return Interest.FINANCE
            case 10:
                return Interest.FOOD
            case 11:
                return Interest.GOVERNMENT
            case 12:
                return Interest.HOBBIES
            case 13:
                return Interest.HOME
            case 14:
                return Interest.NEWS
            case 15:
                return Interest.REAL_ESTATE
            case 16:
                return Interest.SOCIETY
            case 17:
                return Interest.SPORTS
            case 18:
                return Interest.TECH
            case 19:
                return Interest.TRAVEL
            default:
                throw new UniFFITypeError("Unknown Interest variant");
        }
    }

    static write(dataStream, value) {
        if (value === Interest.INCONCLUSIVE) {
            dataStream.writeInt32(1);
            return;
        }
        if (value === Interest.ANIMALS) {
            dataStream.writeInt32(2);
            return;
        }
        if (value === Interest.ARTS) {
            dataStream.writeInt32(3);
            return;
        }
        if (value === Interest.AUTOS) {
            dataStream.writeInt32(4);
            return;
        }
        if (value === Interest.BUSINESS) {
            dataStream.writeInt32(5);
            return;
        }
        if (value === Interest.CAREER) {
            dataStream.writeInt32(6);
            return;
        }
        if (value === Interest.EDUCATION) {
            dataStream.writeInt32(7);
            return;
        }
        if (value === Interest.FASHION) {
            dataStream.writeInt32(8);
            return;
        }
        if (value === Interest.FINANCE) {
            dataStream.writeInt32(9);
            return;
        }
        if (value === Interest.FOOD) {
            dataStream.writeInt32(10);
            return;
        }
        if (value === Interest.GOVERNMENT) {
            dataStream.writeInt32(11);
            return;
        }
        if (value === Interest.HOBBIES) {
            dataStream.writeInt32(12);
            return;
        }
        if (value === Interest.HOME) {
            dataStream.writeInt32(13);
            return;
        }
        if (value === Interest.NEWS) {
            dataStream.writeInt32(14);
            return;
        }
        if (value === Interest.REAL_ESTATE) {
            dataStream.writeInt32(15);
            return;
        }
        if (value === Interest.SOCIETY) {
            dataStream.writeInt32(16);
            return;
        }
        if (value === Interest.SPORTS) {
            dataStream.writeInt32(17);
            return;
        }
        if (value === Interest.TECH) {
            dataStream.writeInt32(18);
            return;
        }
        if (value === Interest.TRAVEL) {
            dataStream.writeInt32(19);
            return;
        }
        throw new UniFFITypeError("Unknown Interest variant");
    }

    static computeSize(value) {
        return 4;
    }

    static checkType(value) {
      if (!Number.isInteger(value) || value < 1 || value > 19) {
          throw new UniFFITypeError(`${value} is not a valid value for Interest`);
      }
    }
}





/**
 * Errors we return via the public interface.
 */
export class RelevancyApiError extends Error {}


/**
 * Unexpected
 */
export class Unexpected extends RelevancyApiError {

    constructor(
        reason,
        ...params
    ) {
        const message = `reason: ${ reason }`;
        super(message, ...params);
        this.reason = reason;
    }
    toString() {
        return `Unexpected: ${super.toString()}`
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRelevancyApiError extends FfiConverterArrayBuffer {
    static read(dataStream) {
        switch (dataStream.readInt32()) {
            case 1:
                return new Unexpected(
                    FfiConverterString.read(dataStream)
                    );
            default:
                throw new UniFFITypeError("Unknown RelevancyApiError variant");
        }
    }
    static computeSize(value) {
        // Size of the Int indicating the variant
        let totalSize = 4;
        if (value instanceof Unexpected) {
            totalSize += FfiConverterString.computeSize(value.reason);
            return totalSize;
        }
        throw new UniFFITypeError("Unknown RelevancyApiError variant");
    }
    static write(dataStream, value) {
        if (value instanceof Unexpected) {
            dataStream.writeInt32(1);
            FfiConverterString.write(dataStream, value.reason);
            return;
        }
        throw new UniFFITypeError("Unknown RelevancyApiError variant");
    }

    static errorClass = RelevancyApiError;
}

// Export the FFIConverter object to make external types work.
export class FfiConverterSequencestring extends FfiConverterArrayBuffer {
    static read(dataStream) {
        const len = dataStream.readInt32();
        const arr = [];
        for (let i = 0; i < len; i++) {
            arr.push(FfiConverterString.read(dataStream));
        }
        return arr;
    }

    static write(dataStream, value) {
        dataStream.writeInt32(value.length);
        value.forEach((innerValue) => {
            FfiConverterString.write(dataStream, innerValue);
        })
    }

    static computeSize(value) {
        // The size of the length
        let size = 4;
        for (const innerValue of value) {
            size += FfiConverterString.computeSize(innerValue);
        }
        return size;
    }

    static checkType(value) {
        if (!Array.isArray(value)) {
            throw new UniFFITypeError(`${value} is not an array`);
        }
        value.forEach((innerValue, idx) => {
            try {
                FfiConverterString.checkType(innerValue);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${idx}]`);
                }
                throw e;
            }
        })
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterSequenceTypeInterest extends FfiConverterArrayBuffer {
    static read(dataStream) {
        const len = dataStream.readInt32();
        const arr = [];
        for (let i = 0; i < len; i++) {
            arr.push(FfiConverterTypeInterest.read(dataStream));
        }
        return arr;
    }

    static write(dataStream, value) {
        dataStream.writeInt32(value.length);
        value.forEach((innerValue) => {
            FfiConverterTypeInterest.write(dataStream, innerValue);
        })
    }

    static computeSize(value) {
        // The size of the length
        let size = 4;
        for (const innerValue of value) {
            size += FfiConverterTypeInterest.computeSize(innerValue);
        }
        return size;
    }

    static checkType(value) {
        if (!Array.isArray(value)) {
            throw new UniFFITypeError(`${value} is not an array`);
        }
        value.forEach((innerValue, idx) => {
            try {
                FfiConverterTypeInterest.checkType(innerValue);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${idx}]`);
                }
                throw e;
            }
        })
    }
}





/**
 * Calculate score for a piece of categorized content based on a user interest vector.
 *
 * This scoring function is of the following properties:
 * - The score ranges from 0.0 to 1.0
 * - The score is monotonically increasing for the accumulated interest count
 *
 * # Params:
 * - `interest_vector`: a user interest vector that can be fetched via
 * `RelevancyStore::user_interest_vector()`.
 * - `content_categories`: a list of categories (interests) of the give content.
 * # Return:
 * - A score ranges in [0, 1].
 * @returns {number}
 */
export function score(interestVector,contentCategories) {

        const liftResult = (result) => FfiConverterF64.lift(result);
        const liftError = null;
        const functionCall = () => {
            try {
                FfiConverterTypeInterestVector.checkType(interestVector)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("interestVector");
                }
                throw e;
            }
            try {
                FfiConverterSequenceTypeInterest.checkType(contentCategories)
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("contentCategories");
                }
                throw e;
            }
            return UniFFIScaffolding.callSync(
                2, // relevancy:uniffi_relevancy_fn_func_score
                FfiConverterTypeInterestVector.lower(interestVector),
                FfiConverterSequenceTypeInterest.lower(contentCategories),
            )
        }
        return handleRustResult(functionCall(), liftResult, liftError);
}
