//! OpenType font variations common tables.

include!("../../generated/generated_variations.rs");

use super::{
    glyf::{PointCoord, PointFlags, PointMarker},
    gvar::GlyphDelta,
};

pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF;
/// Outer and inner indices for reading from an [ItemVariationStore].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeltaSetIndex {
    /// Outer delta set index.
    pub outer: u16,
    /// Inner delta set index.
    pub inner: u16,
}

impl DeltaSetIndex {
    pub const NO_VARIATION_INDEX: Self = Self {
        outer: (NO_VARIATION_INDEX >> 16) as u16,
        inner: (NO_VARIATION_INDEX & 0xFFFF) as u16,
    };
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TupleIndex(u16);

impl TupleIndex {
    /// Flag indicating that this tuple variation header includes an embedded
    /// peak tuple record, immediately after the tupleIndex field.
    ///
    /// If set, the low 12 bits of the tupleIndex value are ignored.
    ///
    /// Note that this must always be set within the 'cvar' table.
    pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000;

    /// Flag indicating that this tuple variation table applies to an
    /// intermediate region within the variation space.
    ///
    /// If set, the header includes the two intermediate-region, start and end
    /// tuple records, immediately after the peak tuple record (if present).
    pub const INTERMEDIATE_REGION: u16 = 0x4000;
    /// Flag indicating that the serialized data for this tuple variation table
    /// includes packed “point” number data.
    ///
    /// If set, this tuple variation table uses that number data; if clear,
    /// this tuple variation table uses shared number data found at the start
    /// of the serialized data for this glyph variation data or 'cvar' table.
    pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000;
    //0x1000	Reserved	Reserved for future use — set to 0.
    //
    /// Mask for the low 12 bits to give the shared tuple records index.
    pub const TUPLE_INDEX_MASK: u16 = 0x0FFF;

    #[inline(always)]
    fn tuple_len(self, axis_count: u16, flag: usize) -> usize {
        if flag == 0 {
            self.embedded_peak_tuple() as usize * axis_count as usize
        } else {
            self.intermediate_region() as usize * axis_count as usize
        }
    }

    pub fn bits(self) -> u16 {
        self.0
    }

    pub fn from_bits(bits: u16) -> Self {
        TupleIndex(bits)
    }

    /// `true` if the header includes an embedded peak tuple.
    pub fn embedded_peak_tuple(self) -> bool {
        (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0
    }

    /// `true` if the header includes the two intermediate region tuple records.
    pub fn intermediate_region(self) -> bool {
        (self.0 & Self::INTERMEDIATE_REGION) != 0
    }

    /// `true` if the data for this table includes packed point number data.
    pub fn private_point_numbers(self) -> bool {
        (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0
    }

    pub fn tuple_records_index(self) -> Option<u16> {
        (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK)
    }
}

impl types::Scalar for TupleIndex {
    type Raw = <u16 as types::Scalar>::Raw;
    fn to_raw(self) -> Self::Raw {
        self.0.to_raw()
    }
    fn from_raw(raw: Self::Raw) -> Self {
        let t = <u16>::from_raw(raw);
        Self(t)
    }
}

/// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header]
///
/// The high 4 bits are flags, and the low 12 bits are the number of tuple
/// variation tables for this glyph. The count can be any number between 1 and 4095.
///
/// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TupleVariationCount(u16);

impl TupleVariationCount {
    /// Flag indicating that some or all tuple variation tables reference a
    /// shared set of “point” numbers.
    ///
    /// These shared numbers are represented as packed point number data at the
    /// start of the serialized data.
    pub const SHARED_POINT_NUMBERS: u16 = 0x8000;

    /// Mask for the low 12 bits to give the shared tuple records index.
    pub const COUNT_MASK: u16 = 0x0FFF;

    pub fn bits(self) -> u16 {
        self.0
    }

    pub fn from_bits(bits: u16) -> Self {
        Self(bits)
    }

    /// `true` if any tables reference a shared set of point numbers
    pub fn shared_point_numbers(self) -> bool {
        (self.0 & Self::SHARED_POINT_NUMBERS) != 0
    }

    pub fn count(self) -> u16 {
        self.0 & Self::COUNT_MASK
    }
}

impl types::Scalar for TupleVariationCount {
    type Raw = <u16 as types::Scalar>::Raw;
    fn to_raw(self) -> Self::Raw {
        self.0.to_raw()
    }
    fn from_raw(raw: Self::Raw) -> Self {
        let t = <u16>::from_raw(raw);
        Self(t)
    }
}

impl<'a> TupleVariationHeader<'a> {
    #[cfg(feature = "experimental_traverse")]
    fn traverse_tuple_index(&self) -> traversal::FieldType<'a> {
        self.tuple_index().0.into()
    }

    /// Peak tuple record for this tuple variation table — optional,
    /// determined by flags in the tupleIndex value.  Note that this
    /// must always be included in the 'cvar' table.
    #[inline(always)]
    pub fn peak_tuple(&self) -> Option<Tuple<'a>> {
        self.tuple_index().embedded_peak_tuple().then(|| {
            let range = self.peak_tuple_byte_range();
            Tuple {
                values: self.data.read_array(range).unwrap(),
            }
        })
    }

    /// Intermediate start tuple record for this tuple variation table
    /// — optional, determined by flags in the tupleIndex value.
    #[inline(always)]
    pub fn intermediate_start_tuple(&self) -> Option<Tuple<'a>> {
        self.tuple_index().intermediate_region().then(|| {
            let range = self.intermediate_start_tuple_byte_range();
            Tuple {
                values: self.data.read_array(range).unwrap(),
            }
        })
    }

    /// Intermediate end tuple record for this tuple variation table
    /// — optional, determined by flags in the tupleIndex value.
    #[inline(always)]
    pub fn intermediate_end_tuple(&self) -> Option<Tuple<'a>> {
        self.tuple_index().intermediate_region().then(|| {
            let range = self.intermediate_end_tuple_byte_range();
            Tuple {
                values: self.data.read_array(range).unwrap(),
            }
        })
    }

    /// Intermediate tuple records for this tuple variation table
    /// — optional, determined by flags in the tupleIndex value.
    #[inline(always)]
    pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> {
        self.tuple_index().intermediate_region().then(|| {
            let start_range = self.intermediate_start_tuple_byte_range();
            let end_range = self.intermediate_end_tuple_byte_range();
            (
                Tuple {
                    values: self.data.read_array(start_range).unwrap(),
                },
                Tuple {
                    values: self.data.read_array(end_range).unwrap(),
                },
            )
        })
    }

    /// Compute the actual length of this table in bytes
    #[inline(always)]
    fn byte_len(&self, axis_count: u16) -> usize {
        const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN;
        let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize;
        let index = self.tuple_index();
        FIXED_LEN
            + if index.embedded_peak_tuple() {
                tuple_byte_len
            } else {
                Default::default()
            }
            + if index.intermediate_region() {
                tuple_byte_len * 2
            } else {
                Default::default()
            }
    }
}

impl Tuple<'_> {
    pub fn len(&self) -> usize {
        self.values().len()
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty()
    }

    #[inline(always)]
    pub fn get(&self, idx: usize) -> Option<F2Dot14> {
        self.values.get(idx).map(BigEndian::get)
    }
}

//FIXME: add an #[extra_traits(..)] attribute!
#[allow(clippy::derivable_impls)]
impl Default for Tuple<'_> {
    fn default() -> Self {
        Self {
            values: Default::default(),
        }
    }
}

/// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers)
#[derive(Clone, Default, Debug)]
pub struct PackedPointNumbers<'a> {
    data: FontData<'a>,
}

impl<'a> PackedPointNumbers<'a> {
    /// read point numbers off the front of this data, returning the remaining data
    pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) {
        let this = PackedPointNumbers { data };
        let total_len = this.total_len();
        let remainder = data.split_off(total_len).unwrap_or_default();
        (this, remainder)
    }

    /// The number of points in this set
    pub fn count(&self) -> u16 {
        self.count_and_count_bytes().0
    }

    /// compute the count, and the number of bytes used to store it
    fn count_and_count_bytes(&self) -> (u16, usize) {
        match self.data.read_at::<u8>(0).unwrap_or(0) {
            0 => (0, 1),
            count @ 1..=127 => (count as u16, 1),
            _ => {
                // "If the high bit of the first byte is set, then a second byte is used.
                // The count is read from interpreting the two bytes as a big-endian
                // uint16 value with the high-order bit masked out."

                let count = self.data.read_at::<u16>(0).unwrap_or_default() & 0x7FFF;
                // a weird case where I'm following fonttools: if the 'use words' bit
                // is set, but the total count is still 0, treat it like 0 first byte
                if count == 0 {
                    (0, 2)
                } else {
                    (count & 0x7FFF, 2)
                }
            }
        }
    }

    /// the number of bytes to encode the packed point numbers
    #[inline(never)]
    fn total_len(&self) -> usize {
        let (n_points, mut n_bytes) = self.count_and_count_bytes();
        if n_points == 0 {
            return n_bytes;
        }
        let mut cursor = self.data.cursor();
        cursor.advance_by(n_bytes);

        let mut n_seen = 0;
        while n_seen < n_points {
            let Some((count, two_bytes)) = read_control_byte(&mut cursor) else {
                return n_bytes;
            };
            let word_size = 1 + usize::from(two_bytes);
            let run_size = word_size * count as usize;
            n_bytes += run_size + 1; // plus the control byte;
            cursor.advance_by(run_size);
            n_seen += count as u16;
        }

        n_bytes
    }

    /// Iterate over the packed points
    pub fn iter(&self) -> PackedPointNumbersIter<'a> {
        let (count, n_bytes) = self.count_and_count_bytes();
        let mut cursor = self.data.cursor();
        cursor.advance_by(n_bytes);
        PackedPointNumbersIter::new(count, cursor)
    }
}

/// An iterator over the packed point numbers data.
#[derive(Clone, Debug)]
pub struct PackedPointNumbersIter<'a> {
    count: u16,
    seen: u16,
    last_val: u16,
    current_run: PointRunIter<'a>,
}

impl<'a> PackedPointNumbersIter<'a> {
    fn new(count: u16, cursor: Cursor<'a>) -> Self {
        PackedPointNumbersIter {
            count,
            seen: 0,
            last_val: 0,
            current_run: PointRunIter {
                remaining: 0,
                two_bytes: false,
                cursor,
            },
        }
    }
}

/// Implements the logic for iterating over the individual runs
#[derive(Clone, Debug)]
struct PointRunIter<'a> {
    remaining: u8,
    two_bytes: bool,
    cursor: Cursor<'a>,
}

impl Iterator for PointRunIter<'_> {
    type Item = u16;

    fn next(&mut self) -> Option<Self::Item> {
        // if no items remain in this run, start the next one.
        while self.remaining == 0 {
            (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?;
        }

        self.remaining -= 1;
        if self.two_bytes {
            self.cursor.read().ok()
        } else {
            self.cursor.read::<u8>().ok().map(|v| v as u16)
        }
    }
}

/// returns the count and the 'uses_two_bytes' flag from the control byte
fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> {
    let control: u8 = cursor.read().ok()?;
    let two_bytes = (control & 0x80) != 0;
    let count = (control & 0x7F) + 1;
    Some((count, two_bytes))
}

impl Iterator for PackedPointNumbersIter<'_> {
    type Item = u16;

    fn next(&mut self) -> Option<Self::Item> {
        // if our count is zero, we keep incrementing forever
        if self.count == 0 {
            let result = self.last_val;
            self.last_val = self.last_val.checked_add(1)?;
            return Some(result);
        }

        if self.count == self.seen {
            return None;
        }
        self.seen += 1;
        self.last_val = self.last_val.checked_add(self.current_run.next()?)?;
        Some(self.last_val)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.count as usize, Some(self.count as usize))
    }
}

// completely unnecessary?
impl ExactSizeIterator for PackedPointNumbersIter<'_> {}

/// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas)
#[derive(Clone, Debug)]
pub struct PackedDeltas<'a> {
    data: FontData<'a>,
    // How many values we expect
    count: Option<usize>,
}

impl<'a> PackedDeltas<'a> {
    pub(crate) fn new(data: FontData<'a>, count: usize) -> Self {
        Self {
            data,
            count: Some(count),
        }
    }

    /// NOTE: this is unbounded, and assumes all of data is deltas.
    #[doc(hidden)] // used by tests in write-fonts
    pub fn consume_all(data: FontData<'a>) -> Self {
        Self { data, count: None }
    }

    pub fn count(&self) -> Option<usize> {
        self.count
    }

    pub fn count_or_compute(&self) -> usize {
        self.count.unwrap_or_else(|| count_all_deltas(self.data))
    }

    pub fn iter(&self) -> DeltaRunIter<'a> {
        DeltaRunIter::new(self.data.cursor(), self.count)
    }

    pub fn fetcher(&self) -> PackedDeltaFetcher<'a> {
        PackedDeltaFetcher::new(self.data.as_bytes(), self.count)
    }

    fn x_deltas(&self) -> DeltaRunIter<'a> {
        let count = self.count_or_compute() / 2;
        DeltaRunIter::new(self.data.cursor(), Some(count))
    }

    fn y_deltas(&self) -> DeltaRunIter<'a> {
        let count = self.count_or_compute();
        DeltaRunIter::new(self.data.cursor(), Some(count)).skip_fast(count / 2)
    }
}

/// Flag indicating that this run contains no data,
/// and that the deltas for this run are all zero.
const DELTAS_ARE_ZERO: u8 = 0x80;
/// Flag indicating the data type for delta values in the run.
const DELTAS_ARE_WORDS: u8 = 0x40;
/// Mask for the low 6 bits to provide the number of delta values in the run, minus one.
const DELTA_RUN_COUNT_MASK: u8 = 0x3F;

/// The type of values for a given delta run (influences the number of bytes per delta)
///
/// The variants are intentionally set to the byte size of the type to allow usage
/// as a multiplier when computing offsets.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DeltaRunType {
    Zero = 0,
    I8 = 1,
    I16 = 2,
    I32 = 4,
}

impl DeltaRunType {
    /// The run type for a given control byte
    pub fn new(control: u8) -> Self {
        // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set,
        // then the following values are 32-bit.
        // <https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md#tuplevalues>
        let are_zero = (control & DELTAS_ARE_ZERO) != 0;
        let are_words = (control & DELTAS_ARE_WORDS) != 0;
        match (are_zero, are_words) {
            (false, false) => Self::I8,
            (false, true) => Self::I16,
            (true, false) => Self::Zero,
            (true, true) => Self::I32,
        }
    }
}

/// Implements the logic for iterating over the individual runs
#[derive(Clone, Debug)]
pub struct DeltaRunIter<'a> {
    limit: Option<usize>, // when None, consume all available data
    remaining_in_run: u8,
    value_type: DeltaRunType,
    cursor: Cursor<'a>,
}

/// A decoding helper that adds packed deltas directly to an output slice.
pub struct PackedDeltaFetcher<'a> {
    data: &'a [u8],
    pos: usize,
    end: usize,
    run_count: usize,
    value_type: DeltaRunType,
    remaining_total: Option<usize>,
}

impl<'a> PackedDeltaFetcher<'a> {
    fn new(data: &'a [u8], count: Option<usize>) -> Self {
        Self {
            data,
            pos: 0,
            end: data.len(),
            run_count: 0,
            value_type: DeltaRunType::I8,
            remaining_total: count,
        }
    }

    #[inline(always)]
    fn ensure_run(&mut self) -> Result<(), ReadError> {
        if self.run_count > 0 {
            return Ok(());
        }
        if self.pos >= self.end {
            return Err(ReadError::OutOfBounds);
        }
        let control = self.data[self.pos];
        self.pos += 1;
        self.run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
        self.value_type = DeltaRunType::new(control);
        let width = self.value_type as usize;
        let needed = self.run_count * width;
        if self.pos + needed > self.end {
            return Err(ReadError::OutOfBounds);
        }
        Ok(())
    }

    pub fn skip(&mut self, mut n: usize) -> Result<(), ReadError> {
        if let Some(remaining_total) = self.remaining_total {
            if n > remaining_total {
                return Err(ReadError::OutOfBounds);
            }
            self.remaining_total = Some(remaining_total - n);
        }
        while n > 0 {
            self.ensure_run()?;
            let take = n.min(self.run_count);
            let width = self.value_type as usize;
            self.pos += take * width;
            self.run_count -= take;
            n -= take;
        }
        Ok(())
    }

    pub fn add_to_f32_scaled(&mut self, out: &mut [f32], scale: f32) -> Result<(), ReadError> {
        let mut remaining = out.len();
        if let Some(remaining_total) = self.remaining_total {
            if remaining > remaining_total {
                return Err(ReadError::OutOfBounds);
            }
            self.remaining_total = Some(remaining_total - remaining);
        }
        let mut idx = 0usize;
        while remaining > 0 {
            self.ensure_run()?;
            let take = remaining.min(self.run_count);
            match self.value_type {
                DeltaRunType::Zero => {
                    // nothing to add
                    idx += take;
                }
                DeltaRunType::I8 => {
                    let bytes = &self.data[self.pos..self.pos + take];
                    for &b in bytes {
                        out[idx] += b as i8 as f32 * scale;
                        idx += 1;
                    }
                    self.pos += take;
                }
                DeltaRunType::I16 => {
                    let bytes = &self.data[self.pos..self.pos + take * 2];
                    for chunk in bytes.chunks_exact(2) {
                        let delta = i16::from_be_bytes([chunk[0], chunk[1]]) as f32;
                        out[idx] += delta * scale;
                        idx += 1;
                    }
                    self.pos += take * 2;
                }
                DeltaRunType::I32 => {
                    let bytes = &self.data[self.pos..self.pos + take * 4];
                    for chunk in bytes.chunks_exact(4) {
                        let delta =
                            i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) as f32;
                        out[idx] += delta * scale;
                        idx += 1;
                    }
                    self.pos += take * 4;
                }
            }
            self.run_count -= take;
            remaining -= take;
        }
        Ok(())
    }
}

/// Counts the number of deltas available in the given data, avoiding
/// excessive reads.
fn count_all_deltas(data: FontData) -> usize {
    let mut count = 0;
    let mut offset = 0;
    while let Ok(control) = data.read_at::<u8>(offset) {
        let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
        count += run_count;
        offset += run_count * DeltaRunType::new(control) as usize + 1;
    }
    count
}

impl<'a> DeltaRunIter<'a> {
    fn new(cursor: Cursor<'a>, limit: Option<usize>) -> Self {
        DeltaRunIter {
            limit,
            remaining_in_run: 0,
            value_type: DeltaRunType::I8,
            cursor,
        }
    }

    pub(crate) fn end(mut self) -> Cursor<'a> {
        if let Some(limit) = self.limit {
            return self.skip_fast(limit).cursor;
        }
        // No limit: jump over runs without decoding values.
        if self.remaining_in_run != 0 {
            if self.value_type != DeltaRunType::Zero {
                self.cursor
                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
            }
            self.remaining_in_run = 0;
        }
        while self.read_next_control().is_some() {
            if self.value_type != DeltaRunType::Zero {
                self.cursor
                    .advance_by(self.remaining_in_run as usize * self.value_type as usize);
            }
            self.remaining_in_run = 0;
        }
        self.cursor
    }

    /// Skips `n` deltas without reading the actual delta values.
    #[inline(always)]
    pub fn skip_fast(mut self, n: usize) -> Self {
        let mut wanted = n;
        let mut remaining = self.remaining_in_run as usize;
        let mut value_type = self.value_type;
        loop {
            if wanted > remaining {
                // Consume the rest of this run and move to the next.
                self.cursor.advance_by(remaining * value_type as usize);
                wanted -= remaining;
                if self.read_next_control().is_none() {
                    self.limit = Some(0);
                    break;
                }
                remaining = self.remaining_in_run as usize;
                value_type = self.value_type;
                continue;
            }
            let consumed = wanted;
            self.remaining_in_run -= consumed as u8;
            self.cursor.advance_by(consumed * value_type as usize);
            if let Some(limit) = self.limit.as_mut() {
                *limit = limit.saturating_sub(n);
            }
            break;
        }
        self
    }

    #[inline(always)]
    fn read_next_control(&mut self) -> Option<()> {
        self.remaining_in_run = 0;
        let control: u8 = self.cursor.read().ok()?;
        self.value_type = DeltaRunType::new(control);
        self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1;
        Some(())
    }
}

impl Iterator for DeltaRunIter<'_> {
    type Item = i32;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(limit) = self.limit {
            if limit == 0 {
                return None;
            }
            self.limit = Some(limit - 1);
        }
        if self.remaining_in_run == 0 {
            self.read_next_control()?;
        }
        self.remaining_in_run -= 1;
        match self.value_type {
            DeltaRunType::Zero => Some(0),
            DeltaRunType::I8 => self.cursor.read::<i8>().ok().map(|v| v as i32),
            DeltaRunType::I16 => self.cursor.read::<i16>().ok().map(|v| v as i32),
            DeltaRunType::I32 => self.cursor.read::<i32>().ok(),
        }
    }
}

/// A helper type for iterating over [`TupleVariationHeader`]s.
pub struct TupleVariationHeaderIter<'a> {
    data: FontData<'a>,
    n_headers: usize,
    current: usize,
    axis_count: u16,
}

impl<'a> TupleVariationHeaderIter<'a> {
    pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self {
        Self {
            data,
            n_headers,
            current: 0,
            axis_count,
        }
    }
}

impl<'a> Iterator for TupleVariationHeaderIter<'a> {
    type Item = Result<TupleVariationHeader<'a>, ReadError>;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        if self.current == self.n_headers {
            return None;
        }
        self.current += 1;
        let next = TupleVariationHeader::read(self.data, self.axis_count);

        let next_len = next
            .as_ref()
            .map(|table| table.byte_len(self.axis_count))
            .unwrap_or(0);
        self.data = self.data.split_off(next_len)?;
        Some(next)
    }
}

#[derive(Clone)]
pub struct TupleVariationData<'a, T> {
    pub(crate) axis_count: u16,
    pub(crate) shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
    pub(crate) shared_point_numbers: Option<PackedPointNumbers<'a>>,
    pub(crate) tuple_count: TupleVariationCount,
    // the data for all the tuple variation headers
    pub(crate) header_data: FontData<'a>,
    // the data for all the tuple bodies
    pub(crate) serialized_data: FontData<'a>,
    pub(crate) _marker: std::marker::PhantomData<fn() -> T>,
}

impl<'a, T> TupleVariationData<'a, T>
where
    T: TupleDelta,
{
    pub fn tuples(&self) -> TupleVariationIter<'a, T> {
        TupleVariationIter {
            current: 0,
            parent: self.clone(),
            header_iter: TupleVariationHeaderIter::new(
                self.header_data,
                self.tuple_count.count() as usize,
                self.axis_count,
            ),
            serialized_data: self.serialized_data,
            _marker: std::marker::PhantomData,
        }
    }

    /// Returns an iterator over all of the pairs of (variation tuple, scalar)
    /// for this glyph that are active for the given set of normalized
    /// coordinates.
    pub fn active_tuples_at(
        &self,
        coords: &'a [F2Dot14],
    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'a {
        ActiveTupleVariationIter {
            coords,
            parent: self.clone(),
            header_iter: TupleVariationHeaderIter::new(
                self.header_data,
                self.tuple_count.count() as usize,
                self.axis_count,
            ),
            serialized_data: self.serialized_data,
            data_offset: 0,
            _marker: std::marker::PhantomData,
        }
    }

    pub(crate) fn tuple_count(&self) -> usize {
        self.tuple_count.count() as usize
    }
}

/// An iterator over the [`TupleVariation`]s for a specific glyph.
pub struct TupleVariationIter<'a, T> {
    current: usize,
    parent: TupleVariationData<'a, T>,
    header_iter: TupleVariationHeaderIter<'a>,
    serialized_data: FontData<'a>,
    _marker: std::marker::PhantomData<fn() -> T>,
}

impl<'a, T> TupleVariationIter<'a, T>
where
    T: TupleDelta,
{
    #[inline(always)]
    fn next_tuple(&mut self) -> Option<TupleVariation<'a, T>> {
        if self.parent.tuple_count() == self.current {
            return None;
        }
        self.current += 1;

        // FIXME: is it okay to discard an error here?
        let header = self.header_iter.next()?.ok()?;
        let data_len = header.variation_data_size() as usize;
        let var_data = self.serialized_data.take_up_to(data_len)?;

        Some(TupleVariation {
            axis_count: self.parent.axis_count,
            header,
            shared_tuples: self.parent.shared_tuples.clone(),
            serialized_data: var_data,
            shared_point_numbers: self.parent.shared_point_numbers.clone(),
            _marker: std::marker::PhantomData,
        })
    }
}

impl<'a, T> Iterator for TupleVariationIter<'a, T>
where
    T: TupleDelta,
{
    type Item = TupleVariation<'a, T>;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        self.next_tuple()
    }
}

/// An iterator over the active [`TupleVariation`]s for a specific glyph
/// for a given set of coordinates.
struct ActiveTupleVariationIter<'a, T> {
    coords: &'a [F2Dot14],
    parent: TupleVariationData<'a, T>,
    header_iter: TupleVariationHeaderIter<'a>,
    serialized_data: FontData<'a>,
    data_offset: usize,
    _marker: std::marker::PhantomData<fn() -> T>,
}

impl<'a, T> Iterator for ActiveTupleVariationIter<'a, T>
where
    T: TupleDelta,
{
    type Item = (TupleVariation<'a, T>, Fixed);

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let header = self.header_iter.next()?.ok()?;
            let data_len = header.variation_data_size() as usize;
            let data_start = self.data_offset;
            let data_end = data_start.checked_add(data_len)?;
            self.data_offset = data_end;
            if let Some(scalar) = compute_scalar(
                &header,
                self.parent.axis_count as usize,
                &self.parent.shared_tuples,
                self.coords,
            ) {
                let var_data = self.serialized_data.slice(data_start..data_end)?;
                return Some((
                    TupleVariation {
                        axis_count: self.parent.axis_count,
                        header,
                        shared_tuples: self.parent.shared_tuples.clone(),
                        serialized_data: var_data,
                        shared_point_numbers: self.parent.shared_point_numbers.clone(),
                        _marker: std::marker::PhantomData,
                    },
                    scalar,
                ));
            }
        }
    }
}

/// A single set of tuple variation data
#[derive(Clone)]
pub struct TupleVariation<'a, T> {
    axis_count: u16,
    header: TupleVariationHeader<'a>,
    shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
    serialized_data: FontData<'a>,
    shared_point_numbers: Option<PackedPointNumbers<'a>>,
    _marker: std::marker::PhantomData<fn() -> T>,
}

impl<'a, T> TupleVariation<'a, T>
where
    T: TupleDelta,
{
    /// Returns true if this tuple provides deltas for all points in a glyph.
    pub fn has_deltas_for_all_points(&self) -> bool {
        if self.header.tuple_index().private_point_numbers() {
            PackedPointNumbers {
                data: self.serialized_data,
            }
            .count()
                == 0
        } else if let Some(shared) = &self.shared_point_numbers {
            shared.count() == 0
        } else {
            false
        }
    }

    pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> {
        let (point_numbers, _) = self.point_numbers_and_packed_deltas();
        point_numbers.iter()
    }

    /// Returns the 'peak' tuple for this variation
    pub fn peak(&self) -> Tuple<'a> {
        self.header
            .tuple_index()
            .tuple_records_index()
            .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok())
            .or_else(|| self.header.peak_tuple())
            .unwrap_or_default()
    }

    pub fn intermediate_start(&self) -> Option<Tuple<'a>> {
        self.header.intermediate_start_tuple()
    }

    pub fn intermediate_end(&self) -> Option<Tuple<'a>> {
        self.header.intermediate_end_tuple()
    }

    /// Compute the fixed point scalar for this tuple at the given location in
    /// variation space.
    ///
    /// The `coords` slice must be of lesser or equal length to the number of
    /// axes. If it is less, missing (trailing) axes will be assumed to have
    /// zero values.
    ///
    /// Returns `None` if this tuple is not applicable at the provided
    /// coordinates (e.g. if the resulting scalar is zero).
    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option<Fixed> {
        compute_scalar(
            &self.header,
            self.axis_count as usize,
            &self.shared_tuples,
            coords,
        )
    }

    /// Compute the floating point scalar for this tuple at the given location
    /// in variation space.
    ///
    /// The `coords` slice must be of lesser or equal length to the number of
    /// axes. If it is less, missing (trailing) axes will be assumed to have
    /// zero values.
    ///
    /// Returns `None` if this tuple is not applicable at the provided
    /// coordinates (e.g. if the resulting scalar is zero).
    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option<f32> {
        let mut scalar = 1.0;
        let peak = self.peak();
        let inter_start = self.header.intermediate_start_tuple();
        let inter_end = self.header.intermediate_end_tuple();
        if peak.len() != self.axis_count as usize {
            return None;
        }
        for i in 0..self.axis_count {
            let i = i as usize;
            let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32;
            let peak = peak.get(i).unwrap_or_default().to_bits() as i32;
            if peak == 0 || peak == coord {
                continue;
            }
            if coord == 0 {
                return None;
            }
            if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) {
                let start = inter_start.get(i).unwrap_or_default().to_bits() as i32;
                let end = inter_end.get(i).unwrap_or_default().to_bits() as i32;
                if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) {
                    continue;
                }
                if coord < start || coord > end {
                    return None;
                }
                if coord < peak {
                    if peak != start {
                        scalar *= (coord - start) as f32 / (peak - start) as f32;
                    }
                } else if peak != end {
                    scalar *= (end - coord) as f32 / (end - peak) as f32;
                }
            } else {
                if coord < peak.min(0) || coord > peak.max(0) {
                    return None;
                }
                scalar *= coord as f32 / peak as f32;
            }
        }
        Some(scalar)
    }

    /// Iterate over the deltas for this tuple.
    ///
    /// This does not account for scaling. Returns only explicitly encoded
    /// deltas, e.g. an omission by IUP will not be present.
    pub fn deltas(&self) -> TupleDeltaIter<'a, T> {
        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
        let count = point_numbers.count() as usize;
        let packed_deltas = if count == 0 {
            PackedDeltas::consume_all(packed_deltas)
        } else {
            PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count })
        };
        TupleDeltaIter::new(&point_numbers, packed_deltas)
    }

    fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) {
        if self.header.tuple_index().private_point_numbers() {
            PackedPointNumbers::split_off_front(self.serialized_data)
        } else {
            (
                self.shared_point_numbers.clone().unwrap_or_default(),
                self.serialized_data,
            )
        }
    }
}

impl TupleVariation<'_, GlyphDelta> {
    /// Reads the set of deltas from this tuple variation.
    ///
    /// This is significantly faster than using the [`Self::deltas`]
    /// method but requires preallocated memory to store deltas and
    /// flags.
    ///
    /// This method should only be used when the tuple variation is dense,
    /// that is, [`Self::has_deltas_for_all_points`] returns true.
    ///
    /// The size of `deltas` must be the same as the target value set to
    /// which the variation is applied. For simple outlines, this is
    /// `num_points + 4` and for composites it is `num_components + 4`
    /// (where the `+ 4` is to accommodate phantom points).
    ///
    /// The `deltas` slice will not be zeroed before accumulation and each
    /// delta will be multiplied by the given `scalar`.
    pub fn accumulate_dense_deltas<D: PointCoord>(
        &self,
        deltas: &mut [Point<D>],
        scalar: Fixed,
    ) -> Result<(), ReadError> {
        let (_, packed_deltas) = self.point_numbers_and_packed_deltas();
        let mut cursor = packed_deltas.cursor();
        if scalar == Fixed::ONE {
            // scalar of 1.0 is common so avoid the costly conversions and
            // multiplications per coord
            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
                delta.x += D::from_i32(new_delta);
            })?;
            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
                delta.y += D::from_i32(new_delta);
            })?;
        } else {
            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
                delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
            })?;
            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
                delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
            })?;
        }
        Ok(())
    }

    /// Reads the set of deltas from this tuple variation.
    ///
    /// This is significantly faster than using the [`Self::deltas`]
    /// method but requires preallocated memory to store deltas and
    /// flags.
    ///
    /// This method should only be used when the tuple variation is sparse,
    /// that is, [`Self::has_deltas_for_all_points`] returns false.
    ///
    /// The size of `deltas` must be the same as the target value set to
    /// which the variation is applied. For simple outlines, this is
    /// `num_points + 4` and for composites it is `num_components + 4`
    /// (where the `+ 4` is to accommodate phantom points).
    ///
    /// The `deltas` and `flags` slices must be the same size. Modifications
    /// to `deltas` will be sparse and for each entry that is modified, the
    /// [PointMarker::HAS_DELTA] marker will be set for the corresponding
    /// entry in the `flags` slice.
    ///
    /// The `deltas` slice will not be zeroed before accumulation and each
    /// delta will be multiplied by the given `scalar`.
    pub fn accumulate_sparse_deltas<D: PointCoord>(
        &self,
        deltas: &mut [Point<D>],
        flags: &mut [PointFlags],
        scalar: Fixed,
    ) -> Result<(), ReadError> {
        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
        let mut cursor = packed_deltas.cursor();
        let count = point_numbers.count() as usize;
        if scalar == Fixed::ONE {
            // scalar of 1.0 is common so avoid the costly conversions and
            // multiplications per coord
            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
                    delta.x += D::from_i32(new_delta);
                    flag.set_marker(PointMarker::HAS_DELTA);
                }
            })?;
            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
                if let Some(delta) = deltas.get_mut(ix) {
                    delta.y += D::from_i32(new_delta);
                }
            })?;
        } else {
            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
                    delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
                    flag.set_marker(PointMarker::HAS_DELTA);
                }
            })?;
            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
                if let Some(delta) = deltas.get_mut(ix) {
                    delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
                }
            })?;
        }
        Ok(())
    }
}

/// This is basically a manually applied loop unswitching optimization
/// for reading deltas. It reads each typed run into a slice for processing
/// instead of handling each delta individually with all the necessary
/// branching that implies.
fn read_dense_deltas<T>(
    cursor: &mut Cursor,
    deltas: &mut [T],
    mut f: impl FnMut(&mut T, i32),
) -> Result<(), ReadError> {
    let count = deltas.len();
    let mut cur = 0;
    while cur < count {
        let control: u8 = cursor.read()?;
        let value_type = DeltaRunType::new(control);
        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
        let dest = deltas
            .get_mut(cur..cur + run_count)
            .ok_or(ReadError::OutOfBounds)?;
        match value_type {
            DeltaRunType::Zero => {}
            DeltaRunType::I8 => {
                let packed_deltas = cursor.read_array::<i8>(run_count)?;
                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
                    f(delta, *new_delta as i32);
                }
            }
            DeltaRunType::I16 => {
                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
                    f(delta, new_delta.get() as i32);
                }
            }
            DeltaRunType::I32 => {
                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
                    f(delta, new_delta.get());
                }
            }
        }
        cur += run_count;
    }
    Ok(())
}

/// See [read_dense_deltas] docs.
fn read_sparse_deltas(
    cursor: &mut Cursor,
    point_numbers: &PackedPointNumbers,
    count: usize,
    mut f: impl FnMut(usize, i32),
) -> Result<(), ReadError> {
    let mut cur = 0;
    let mut points_iter = point_numbers.iter().map(|ix| ix as usize);
    while cur < count {
        let control: u8 = cursor.read()?;
        let value_type = DeltaRunType::new(control);
        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
        match value_type {
            DeltaRunType::Zero => {
                for _ in 0..run_count {
                    let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?;
                    f(point_ix, 0);
                }
            }
            DeltaRunType::I8 => {
                let packed_deltas = cursor.read_array::<i8>(run_count)?;
                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
                    f(point_ix, *new_delta as i32);
                }
            }
            DeltaRunType::I16 => {
                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
                    f(point_ix, new_delta.get() as i32);
                }
            }
            DeltaRunType::I32 => {
                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
                    f(point_ix, new_delta.get());
                }
            }
        }
        cur += run_count;
    }
    Ok(())
}

/// Compute the fixed point scalar for this tuple at the given location in
/// variation space.
///
/// The `coords` slice must be of lesser or equal length to the number of
/// axes. If it is less, missing (trailing) axes will be assumed to have
/// zero values.
///
/// Returns `None` if this tuple is not applicable at the provided
/// coordinates (e.g. if the resulting scalar is zero).
#[inline(always)]
fn compute_scalar<'a>(
    header: &TupleVariationHeader,
    axis_count: usize,
    shared_tuples: &Option<ComputedArray<'a, Tuple<'a>>>,
    coords: &[F2Dot14],
) -> Option<Fixed> {
    let mut scalar = Fixed::ONE;
    let tuple_idx = header.tuple_index();
    let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() {
        shared_tuples.as_ref()?.get(shared_index as usize).ok()?
    } else {
        header.peak_tuple()?
    };
    if peak.len() != axis_count {
        return None;
    }
    let intermediate = header.intermediate_tuples();
    for (i, peak) in peak
        .values
        .iter()
        .enumerate()
        .filter(|(_, peak)| peak.get() != F2Dot14::ZERO)
    {
        let coord = coords.get(i).copied().unwrap_or_default();
        if coord == F2Dot14::ZERO {
            return None;
        }
        let peak = peak.get();
        if peak == coord {
            continue;
        }
        if let Some((inter_start, inter_end)) = &intermediate {
            let start = inter_start.get(i).unwrap_or_default();
            let end = inter_end.get(i).unwrap_or_default();
            if coord <= start || coord >= end {
                return None;
            }
            let coord = coord.to_fixed();
            let peak = peak.to_fixed();
            if coord < peak {
                let start = start.to_fixed();
                scalar = scalar.mul_div(coord - start, peak - start);
            } else {
                let end = end.to_fixed();
                scalar = scalar.mul_div(end - coord, end - peak);
            }
        } else {
            if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) {
                return None;
            }
            let coord = coord.to_fixed();
            let peak = peak.to_fixed();
            scalar = scalar.mul_div(coord, peak);
        }
    }
    (scalar != Fixed::ZERO).then_some(scalar)
}

#[derive(Clone, Debug)]
enum TupleDeltaValues<'a> {
    // Point deltas have separate runs for x and y coordinates.
    Points(DeltaRunIter<'a>, DeltaRunIter<'a>),
    Scalars(DeltaRunIter<'a>),
}

/// An iterator over the deltas for a glyph.
#[derive(Clone, Debug)]
pub struct TupleDeltaIter<'a, T> {
    pub cur: usize,
    // if None all points get deltas, if Some specifies subset of points that do
    points: Option<PackedPointNumbersIter<'a>>,
    next_point: usize,
    values: TupleDeltaValues<'a>,
    _marker: std::marker::PhantomData<fn() -> T>,
}

impl<'a, T> TupleDeltaIter<'a, T>
where
    T: TupleDelta,
{
    fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> {
        let mut points = points.iter();
        let next_point = points.next();
        let values = if T::is_point() {
            TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas())
        } else {
            TupleDeltaValues::Scalars(deltas.iter())
        };
        TupleDeltaIter {
            cur: 0,
            points: next_point.map(|_| points),
            next_point: next_point.unwrap_or_default() as usize,
            values,
            _marker: std::marker::PhantomData,
        }
    }
}

/// Trait for deltas that are computed in a tuple variation store.
pub trait TupleDelta: Sized + Copy + 'static {
    /// Returns true if the delta is a point and requires reading two values
    /// from the packed delta stream.
    fn is_point() -> bool;

    /// Creates a new delta for the given position and coordinates. If
    /// the delta is not a point, the y value will always be zero.
    fn new(position: u16, x: i32, y: i32) -> Self;
}

impl<T> Iterator for TupleDeltaIter<'_, T>
where
    T: TupleDelta,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        let (position, dx, dy) = loop {
            let position = if let Some(points) = &mut self.points {
                // if we have points then result is sparse; only some points have deltas
                if self.cur > self.next_point {
                    self.next_point = points.next()? as usize;
                }
                self.next_point
            } else {
                // no points, every point has a delta. Just take the next one.
                self.cur
            };
            if position == self.cur {
                let (dx, dy) = match &mut self.values {
                    TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?),
                    TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0),
                };
                break (position, dx, dy);
            }
            self.cur += 1;
        };
        self.cur += 1;
        Some(T::new(position as u16, dx, dy))
    }
}

impl EntryFormat {
    pub fn entry_size(self) -> u8 {
        ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1
    }

    pub fn bit_count(self) -> u8 {
        (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1
    }

    // called from codegen
    pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize {
        self.entry_size() as usize * map_count.into() as usize
    }
}

impl DeltaSetIndexMap<'_> {
    /// Returns the delta set index for the specified value.
    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
        let (entry_format, map_count, data) = match self {
            Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()),
            Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()),
        };
        if map_count == 0 {
            return Ok(DeltaSetIndex {
                outer: (index >> 16) as u16,
                inner: index as u16,
            });
        }
        let entry_size = entry_format.entry_size();
        let data = FontData::new(data);
        // "if an index into the mapping array is used that is greater than or equal to
        // mapCount, then the last logical entry of the mapping array is used."
        // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
        // #associating-target-items-to-variation-data
        let index = index.min(map_count.saturating_sub(1));
        let offset = index as usize * entry_size as usize;
        let entry = match entry_size {
            1 => data.read_at::<u8>(offset)? as u32,
            2 => data.read_at::<u16>(offset)? as u32,
            3 => data.read_at::<Uint24>(offset)?.into(),
            4 => data.read_at::<u32>(offset)?,
            _ => {
                return Err(ReadError::MalformedData(
                    "invalid entry size in DeltaSetIndexMap",
                ))
            }
        };
        let bit_count = entry_format.bit_count();
        Ok(DeltaSetIndex {
            outer: (entry >> bit_count) as u16,
            inner: (entry & ((1 << bit_count) - 1)) as u16,
        })
    }
}

impl ItemVariationStore<'_> {
    /// Computes the delta value for the specified index and set of normalized
    /// variation coordinates.
    pub fn compute_delta(
        &self,
        index: DeltaSetIndex,
        coords: &[F2Dot14],
    ) -> Result<i32, ReadError> {
        if coords.is_empty() || index == DeltaSetIndex::NO_VARIATION_INDEX {
            return Ok(0);
        }
        let data = match self.item_variation_data().get(index.outer as usize) {
            Some(data) => data?,
            None => return Ok(0),
        };
        let regions = self.variation_region_list()?.variation_regions();
        let region_indices = data.region_indexes();
        // Compute deltas with 64-bit precision.
        // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/7ab541a2/src/truetype/ttgxvar.c#L1094>
        let mut accum = 0i64;
        for (i, region_delta) in data.delta_set(index.inner).enumerate() {
            let region_index = region_indices
                .get(i)
                .ok_or(ReadError::MalformedData(
                    "invalid delta sets in ItemVariationStore",
                ))?
                .get() as usize;
            let region = regions.get(region_index)?;
            let scalar = region.compute_scalar(coords);
            accum += region_delta as i64 * scalar.to_bits() as i64;
        }
        Ok(((accum + 0x8000) >> 16) as i32)
    }

    /// Computes the delta value in floating point for the specified index and set
    /// of normalized variation coordinates.
    pub fn compute_float_delta(
        &self,
        index: DeltaSetIndex,
        coords: &[F2Dot14],
    ) -> Result<FloatItemDelta, ReadError> {
        if coords.is_empty() {
            return Ok(FloatItemDelta::ZERO);
        }
        let data = match self.item_variation_data().get(index.outer as usize) {
            Some(data) => data?,
            None => return Ok(FloatItemDelta::ZERO),
        };
        let regions = self.variation_region_list()?.variation_regions();
        let region_indices = data.region_indexes();
        // Compute deltas in 64-bit floating point.
        let mut accum = 0f64;
        for (i, region_delta) in data.delta_set(index.inner).enumerate() {
            let region_index = region_indices
                .get(i)
                .ok_or(ReadError::MalformedData(
                    "invalid delta sets in ItemVariationStore",
                ))?
                .get() as usize;
            let region = regions.get(region_index)?;
            let scalar = region.compute_scalar_f32(coords);
            accum += region_delta as f64 * scalar as f64;
        }
        Ok(FloatItemDelta(accum))
    }
}

/// Floating point item delta computed by an item variation store.
///
/// These can be applied to types that implement [`FloatItemDeltaTarget`].
#[derive(Copy, Clone, Default, Debug)]
pub struct FloatItemDelta(f64);

impl FloatItemDelta {
    pub const ZERO: Self = Self(0.0);

    pub(crate) fn to_f64(self) -> f64 {
        self.0
    }
}

/// Trait for applying floating point item deltas to target values.
pub trait FloatItemDeltaTarget {
    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32;
}

impl FloatItemDeltaTarget for Fixed {
    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
        const FIXED_TO_FLOAT: f64 = 1.0 / 65536.0;
        self.to_f32() + (delta.0 * FIXED_TO_FLOAT) as f32
    }
}

impl FloatItemDeltaTarget for FWord {
    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
        self.to_i16() as f32 + delta.0 as f32
    }
}

impl FloatItemDeltaTarget for UfWord {
    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
        self.to_u16() as f32 + delta.0 as f32
    }
}

impl FloatItemDeltaTarget for F2Dot14 {
    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
        const F2DOT14_TO_FLOAT: f64 = 1.0 / 16384.0;
        self.to_f32() + (delta.0 * F2DOT14_TO_FLOAT) as f32
    }
}

impl<'a> VariationRegion<'a> {
    /// Computes a scalar value for this region and the specified
    /// normalized variation coordinates.
    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
        const ZERO: Fixed = Fixed::ZERO;
        let mut scalar = Fixed::ONE;
        for (i, peak, axis_coords) in self.active_region_axes() {
            let peak = peak.to_fixed();
            let start = axis_coords.start_coord.get().to_fixed();
            let end = axis_coords.end_coord.get().to_fixed();
            if start > peak || peak > end || start < ZERO && end > ZERO {
                continue;
            }
            let coord = coords.get(i).map(|coord| coord.to_fixed()).unwrap_or(ZERO);
            if coord < start || coord > end {
                return ZERO;
            } else if coord == peak {
                continue;
            } else if coord < peak {
                scalar = scalar.mul_div(coord - start, peak - start);
            } else {
                scalar = scalar.mul_div(end - coord, end - peak);
            }
        }
        scalar
    }

    /// Computes a floating point scalar value for this region and the
    /// specified normalized variation coordinates.
    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> f32 {
        let mut scalar = 1.0;
        for (i, peak, axis_coords) in self.active_region_axes() {
            let peak = peak.to_f32();
            let start = axis_coords.start_coord.get().to_f32();
            let end = axis_coords.end_coord.get().to_f32();
            if start > peak || peak > end || start < 0.0 && end > 0.0 {
                continue;
            }
            let coord = coords.get(i).map(|coord| coord.to_f32()).unwrap_or(0.0);
            if coord < start || coord > end {
                return 0.0;
            } else if coord == peak {
                continue;
            } else if coord < peak {
                scalar = (scalar * (coord - start)) / (peak - start);
            } else {
                scalar = (scalar * (end - coord)) / (end - peak);
            }
        }
        scalar
    }

    fn active_region_axes(
        &self,
    ) -> impl Iterator<Item = (usize, F2Dot14, &'a RegionAxisCoordinates)> {
        self.region_axes()
            .iter()
            .enumerate()
            .filter_map(|(i, axis_coords)| {
                let peak = axis_coords.peak_coord();
                if peak != F2Dot14::ZERO {
                    Some((i, peak, axis_coords))
                } else {
                    None
                }
            })
    }
}

impl<'a> ItemVariationData<'a> {
    /// Returns an iterator over the per-region delta values for the specified
    /// inner index.
    pub fn delta_set(&self, inner_index: u16) -> impl Iterator<Item = i32> + 'a + Clone {
        let word_delta_count = self.word_delta_count();
        let region_count = self.region_index_count();
        let bytes_per_row = Self::delta_row_len(word_delta_count, region_count);
        let long_words = word_delta_count & 0x8000 != 0;
        let word_delta_count = word_delta_count & 0x7FFF;

        let offset = bytes_per_row * inner_index as usize;
        ItemDeltas {
            cursor: FontData::new(self.delta_sets())
                .slice(offset..)
                .unwrap_or_default()
                .cursor(),
            word_delta_count,
            long_words,
            len: region_count,
            pos: 0,
        }
    }

    pub fn get_delta_row_len(&self) -> usize {
        let word_delta_count = self.word_delta_count();
        let region_count = self.region_index_count();
        Self::delta_row_len(word_delta_count, region_count)
    }

    /// the length of one delta set
    pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize {
        let region_count = region_index_count as usize;
        let long_words = word_delta_count & 0x8000 != 0;
        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
        let long_delta_count = (word_delta_count & 0x7FFF) as usize;
        let short_delta_count = region_count.saturating_sub(long_delta_count);
        long_delta_count * word_size + short_delta_count * small_size
    }

    // called from generated code: compute the length in bytes of the delta_sets data
    pub fn delta_sets_len(
        item_count: u16,
        word_delta_count: u16,
        region_index_count: u16,
    ) -> usize {
        let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count);
        bytes_per_row * item_count as usize
    }
}

#[derive(Clone)]
struct ItemDeltas<'a> {
    cursor: Cursor<'a>,
    word_delta_count: u16,
    long_words: bool,
    len: u16,
    pos: u16,
}

impl Iterator for ItemDeltas<'_> {
    type Item = i32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.len {
            return None;
        }
        let pos = self.pos;
        self.pos += 1;
        let value = match (pos >= self.word_delta_count, self.long_words) {
            (true, true) | (false, false) => self.cursor.read::<i16>().ok()? as i32,
            (true, false) => self.cursor.read::<i8>().ok()? as i32,
            (false, true) => self.cursor.read::<i32>().ok()?,
        };
        Some(value)
    }
}

pub(crate) fn advance_delta(
    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
    ivs: Result<ItemVariationStore, ReadError>,
    glyph_id: GlyphId,
    coords: &[F2Dot14],
) -> Result<Fixed, ReadError> {
    if coords.is_empty() {
        return Ok(Fixed::ZERO);
    }
    let gid = glyph_id.to_u32();
    let ix = match dsim {
        Some(Ok(dsim)) => dsim.get(gid)?,
        _ => DeltaSetIndex {
            outer: 0,
            inner: gid as _,
        },
    };
    Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?))
}

pub(crate) fn item_delta(
    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
    ivs: Result<ItemVariationStore, ReadError>,
    glyph_id: GlyphId,
    coords: &[F2Dot14],
) -> Result<Fixed, ReadError> {
    if coords.is_empty() {
        return Ok(Fixed::ZERO);
    }
    let gid = glyph_id.to_u32();
    let ix = match dsim {
        Some(Ok(dsim)) => dsim.get(gid)?,
        _ => return Err(ReadError::NullOffset),
    };
    Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?))
}

#[cfg(test)]
mod tests {
    use font_test_data::bebuffer::BeBuffer;

    use super::*;
    use crate::{FontRef, TableProvider};

    #[test]
    fn ivs_regions() {
        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
        let hvar = font.hvar().expect("missing HVAR table");
        let ivs = hvar
            .item_variation_store()
            .expect("missing item variation store in HVAR");
        let region_list = ivs.variation_region_list().expect("missing region list!");
        let regions = region_list.variation_regions();
        let expected = &[
            // start_coord, peak_coord, end_coord
            vec![[-1.0f32, -1.0, 0.0]],
            vec![[0.0, 1.0, 1.0]],
        ][..];
        let region_coords = regions
            .iter()
            .map(|region| {
                region
                    .unwrap()
                    .region_axes()
                    .iter()
                    .map(|coords| {
                        [
                            coords.start_coord().to_f32(),
                            coords.peak_coord().to_f32(),
                            coords.end_coord().to_f32(),
                        ]
                    })
                    .collect::<Vec<_>>()
            })
            .collect::<Vec<_>>();
        assert_eq!(expected, &region_coords);
    }

    // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492
    #[test]
    fn packed_points() {
        fn decode_points(bytes: &[u8]) -> Option<Vec<u16>> {
            let data = FontData::new(bytes);
            let packed = PackedPointNumbers { data };
            if packed.count() == 0 {
                None
            } else {
                Some(packed.iter().collect())
            }
        }

        assert_eq!(decode_points(&[0]), None);
        // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec)
        assert_eq!(decode_points(&[0x80, 0]), None);
        // 2 points; first run: [9, 9+6]
        assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15]));
        // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF)
        assert_eq!(
            decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]),
            Some(vec![0xbeef, 0xcafe])
        );
        // 1 point; first run: [7]
        assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7]));
        // 1 point; first run: [7] in overly verbose encoding
        assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7]));
        // 1 point; first run: [65535]; requires words to be treated as unsigned numbers
        assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535]));
        // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2).
        assert_eq!(
            decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]),
            Some(vec![7, 8, 263, 265])
        );
    }

    #[test]
    fn packed_point_byte_len() {
        fn count_bytes(bytes: &[u8]) -> usize {
            let packed = PackedPointNumbers {
                data: FontData::new(bytes),
            };
            packed.total_len()
        }

        static CASES: &[&[u8]] = &[
            &[0],
            &[0x80, 0],
            &[0x02, 0x01, 0x09, 0x06],
            &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f],
            &[0x01, 0, 0x07],
            &[0x01, 0x80, 0, 0x07],
            &[0x01, 0x80, 0xff, 0xff],
            &[0x04, 1, 7, 1, 1, 0xff, 2],
        ];

        for case in CASES {
            assert_eq!(count_bytes(case), case.len(), "{case:?}");
        }
    }

    // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670
    #[test]
    fn packed_deltas() {
        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);

        let deltas = PackedDeltas::consume_all(INPUT);
        assert_eq!(deltas.count_or_compute(), 7);
        assert_eq!(
            deltas.iter().collect::<Vec<_>>(),
            &[0, 0, 0, 0, 258, -127, -128]
        );

        assert_eq!(
            PackedDeltas::consume_all(FontData::new(&[0x81]))
                .iter()
                .collect::<Vec<_>>(),
            &[0, 0,]
        );
    }

    // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
    #[test]
    fn packed_deltas_spec() {
        static INPUT: FontData = FontData::new(&[
            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
        ]);
        static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228];

        let deltas = PackedDeltas::consume_all(INPUT);
        assert_eq!(deltas.count_or_compute(), EXPECTED.len());
        assert_eq!(deltas.iter().collect::<Vec<_>>(), EXPECTED);
    }

    #[test]
    fn packed_delta_fetcher_skip_matches_iterator_suffix() {
        static INPUT: FontData = FontData::new(&[
            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
        ]);
        let deltas = PackedDeltas::consume_all(INPUT);
        let expected = deltas.iter().collect::<Vec<_>>();

        for skip in 0..=expected.len() {
            let mut fetcher = deltas.fetcher();
            fetcher.skip(skip).unwrap();
            let mut out = vec![0.0; expected.len() - skip];
            fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
            let got = out.into_iter().map(|v| v as i32).collect::<Vec<_>>();
            assert_eq!(&got[..], &expected[skip..], "skip={skip}");
        }

        let mut fetcher = deltas.fetcher();
        assert!(matches!(
            fetcher.skip(expected.len() + 1),
            Err(ReadError::OutOfBounds)
        ));
    }

    #[test]
    fn packed_delta_fetcher_scaled_add_and_exhaustion() {
        static INPUT: FontData = FontData::new(&[
            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
        ]);
        // First four deltas are [10, -105, 0, -58].
        let deltas = PackedDeltas::new(INPUT, 4);
        let mut fetcher = deltas.fetcher();
        let mut out = [1.0f32; 4];
        fetcher.add_to_f32_scaled(&mut out, 0.5).unwrap();
        assert_eq!(out, [6.0, -51.5, 1.0, -28.0]);

        // Bounded fetcher should now be exhausted.
        let mut extra = [0.0f32; 1];
        assert!(matches!(
            fetcher.add_to_f32_scaled(&mut extra, 1.0),
            Err(ReadError::OutOfBounds)
        ));
    }

    #[test]
    fn packed_delta_fetcher_skip_then_add_bounded() {
        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
        // Full decoded stream: [0, 0, 0, 0, 258, -127, -128]
        let deltas = PackedDeltas::new(INPUT, 7);
        let mut fetcher = deltas.fetcher();
        fetcher.skip(3).unwrap();
        let mut out = [0.0f32; 4];
        fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap();
        assert_eq!(out, [0.0, 258.0, -127.0, -128.0]);
    }

    #[test]
    fn delta_run_iter_end_exhausts_unbounded_data() {
        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
        let deltas = PackedDeltas::consume_all(INPUT);
        let end = deltas.iter().end();
        assert_eq!(end.remaining_bytes(), 0);
    }

    #[test]
    fn delta_run_iter_end_respects_bounded_count() {
        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
        // Count is exactly the first run only (4 zeros), so end() should not consume past
        // the run header byte.
        let deltas = PackedDeltas::new(INPUT, 4);
        let end = deltas.iter().end();
        assert_eq!(end.remaining_bytes(), INPUT.len() - 1);

        let end_via_skip = deltas.iter().skip_fast(4).cursor;
        assert_eq!(end_via_skip.remaining_bytes(), INPUT.len() - 1);
    }

    #[test]
    fn delta_run_iter_end_matches_manual_iteration_for_bounded_data() {
        static INPUT: FontData = FontData::new(&[
            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
        ]);
        let deltas = PackedDeltas::new(INPUT, 6);

        let iter_collected = deltas.iter().collect::<Vec<_>>();
        assert_eq!(iter_collected.len(), 6);

        let end = deltas.iter().end();
        let end_via_skip = deltas.iter().skip_fast(6).cursor;
        assert_eq!(end.remaining_bytes(), end_via_skip.remaining_bytes());
    }

    fn lcg_next(state: &mut u32) -> u32 {
        *state = state.wrapping_mul(1664525).wrapping_add(1013904223);
        *state
    }

    fn generated_delta_stream(seed: u32) -> (Vec<u8>, Vec<i32>) {
        let mut state = seed;
        let mut bytes = Vec::new();
        let mut expected = Vec::new();
        let run_count = (lcg_next(&mut state) % 6 + 1) as usize;
        for _ in 0..run_count {
            let run_type = (lcg_next(&mut state) % 4) as usize;
            let len = (lcg_next(&mut state) % 8 + 1) as usize;
            let control = match run_type {
                0 => (len - 1) as u8,        // i8
                1 => 0x40 | (len - 1) as u8, // i16
                2 => 0x80 | (len - 1) as u8, // zero
                _ => 0xC0 | (len - 1) as u8, // i32
            };
            bytes.push(control);
            match run_type {
                0 => {
                    for _ in 0..len {
                        let v = ((lcg_next(&mut state) % 255) as i32 - 127) as i8;
                        bytes.push(v as u8);
                        expected.push(v as i32);
                    }
                }
                1 => {
                    for _ in 0..len {
                        let v = ((lcg_next(&mut state) % 65535) as i32 - 32767) as i16;
                        bytes.extend(v.to_be_bytes());
                        expected.push(v as i32);
                    }
                }
                2 => {
                    expected.resize(expected.len() + len, 0);
                }
                _ => {
                    for _ in 0..len {
                        let v = (lcg_next(&mut state) % 2_000_001) as i32 - 1_000_000;
                        bytes.extend(v.to_be_bytes());
                        expected.push(v);
                    }
                }
            }
        }
        (bytes, expected)
    }

    #[test]
    fn generated_packed_deltas_iter_matches_expected() {
        for seed in 1..=64 {
            let (bytes, expected) = generated_delta_stream(seed);
            let data = FontData::new(&bytes);
            let deltas = PackedDeltas::consume_all(data);
            assert_eq!(deltas.count_or_compute(), expected.len(), "seed={seed}");
            assert_eq!(deltas.iter().collect::<Vec<_>>(), expected, "seed={seed}");
        }
    }

    #[test]
    fn generated_fetcher_skip_scaled_matches_expected() {
        for seed in 1..=64 {
            let (bytes, expected) = generated_delta_stream(seed);
            let data = FontData::new(&bytes);
            let deltas = PackedDeltas::new(data, expected.len());
            let mut fetcher = deltas.fetcher();
            let skip = (seed as usize * 7) % (expected.len() + 1);
            fetcher.skip(skip).unwrap();

            let scale = if seed % 2 == 0 { 0.25 } else { -0.5 };
            let mut out = vec![10.0f32; expected.len() - skip];
            fetcher.add_to_f32_scaled(&mut out, scale).unwrap();
            for (i, got) in out.iter().copied().enumerate() {
                let want = 10.0 + expected[skip + i] as f32 * scale;
                assert!(
                    (got - want).abs() <= 1e-6,
                    "seed={seed} i={i} got={got} want={want}"
                );
            }

            // Bounded fetcher should be exhausted after consuming all remaining entries.
            let mut extra = [0.0f32; 1];
            assert!(matches!(
                fetcher.add_to_f32_scaled(&mut extra, 1.0),
                Err(ReadError::OutOfBounds)
            ));
        }
    }

    #[test]
    fn packed_point_split() {
        static INPUT: FontData =
            FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]);
        let (points, data) = PackedPointNumbers::split_off_front(INPUT);
        assert_eq!(points.count(), 2);
        assert_eq!(points.iter().collect::<Vec<_>>(), &[1, 3]);
        assert_eq!(points.total_len(), 4);
        assert_eq!(data.len(), INPUT.len() - 4);
    }

    #[test]
    fn packed_points_dont_panic() {
        // a single '0' byte means that there are deltas for all points
        static ALL_POINTS: FontData = FontData::new(&[0]);
        let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS);
        // in which case the iterator just keeps incrementing until u16::MAX
        assert_eq!(all_points.iter().count(), u16::MAX as usize);
    }

    /// Test that we split properly when the coordinate boundary doesn't align
    /// with a packed run boundary
    #[test]
    fn packed_delta_run_crosses_coord_boundary() {
        // 8 deltas with values 0..=7 with a run broken after the first 6; the
        // coordinate boundary occurs after the first 4
        static INPUT: FontData = FontData::new(&[
            // first run: 6 deltas as bytes
            5,
            0,
            1,
            2,
            3,
            // coordinate boundary is here
            4,
            5,
            // second run: 2 deltas as words
            1 | DELTAS_ARE_WORDS,
            0,
            6,
            0,
            7,
        ]);
        let deltas = PackedDeltas::consume_all(INPUT);
        assert_eq!(deltas.count_or_compute(), 8);
        let x_deltas = deltas.x_deltas().collect::<Vec<_>>();
        let y_deltas = deltas.y_deltas().collect::<Vec<_>>();
        assert_eq!(x_deltas, [0, 1, 2, 3]);
        assert_eq!(y_deltas, [4, 5, 6, 7]);
    }

    /// We don't have a reference for our float delta computation, so this is
    /// a sanity test to ensure that floating point deltas are within a
    /// reasonable margin of the same in fixed point.
    #[test]
    fn ivs_float_deltas_nearly_match_fixed_deltas() {
        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
        let axis_count = font.fvar().unwrap().axis_count() as usize;
        let colr = font.colr().unwrap();
        let ivs = colr.item_variation_store().unwrap().unwrap();
        // Generate a set of coords from -1 to 1 in 0.1 increments
        for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) {
            // For testing purposes, just splat the coord to all axes
            let coords = vec![coord; axis_count];
            for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() {
                let outer_ix = outer_ix as u16;
                let Some(Ok(data)) = data else {
                    continue;
                };
                for inner_ix in 0..data.item_count() {
                    let delta_ix = DeltaSetIndex {
                        outer: outer_ix,
                        inner: inner_ix,
                    };
                    // Check the deltas against all possible target values
                    let orig_delta = ivs.compute_delta(delta_ix, &coords).unwrap();
                    let float_delta = ivs.compute_float_delta(delta_ix, &coords).unwrap();
                    // For font unit types, we need to accept both rounding and
                    // truncation to account for the additional accumulation of
                    // fractional bits in floating point
                    assert!(
                        orig_delta == float_delta.0.round() as i32
                            || orig_delta == float_delta.0.trunc() as i32
                    );
                    // For the fixed point types, check with an epsilon
                    const EPSILON: f32 = 1e12;
                    let fixed_delta = Fixed::ZERO.apply_float_delta(float_delta);
                    assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON);
                    let f2dot14_delta = F2Dot14::ZERO.apply_float_delta(float_delta);
                    assert!(
                        (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs()
                            < EPSILON
                    );
                }
            }
        }
    }

    #[test]
    fn ivs_data_len_short() {
        let data = BeBuffer::new()
            .push(2u16) // item_count
            .push(3u16) // word_delta_count
            .push(5u16) // region_index_count
            .extend([0u16, 1, 2, 3, 4]) // region_indices
            .extend([1u8; 128]); // this is much more data than we need!

        let ivs = ItemVariationData::read(data.data().into()).unwrap();
        let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas
        let expected_len = 2 * row_len;
        assert_eq!(ivs.delta_sets().len(), expected_len);
    }

    #[test]
    fn ivs_data_len_long() {
        let data = BeBuffer::new()
            .push(2u16) // item_count
            .push(2u16 | 0x8000) // word_delta_count, long deltas
            .push(4u16) // region_index_count
            .extend([0u16, 1, 2]) // region_indices
            .extend([1u8; 128]); // this is much more data than we need!

        let ivs = ItemVariationData::read(data.data().into()).unwrap();
        let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte)
        let expected_len = 2 * row_len;
        assert_eq!(ivs.delta_sets().len(), expected_len);
    }

    // Add with overflow when accumulating packed point numbers
    // https://issues.oss-fuzz.com/issues/378159154
    #[test]
    fn packed_point_numbers_avoid_overflow() {
        // Lots of 1 bits triggers the behavior quite nicely
        let buf = vec![0xFF; 0xFFFF];
        let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor());
        // Don't panic!
        let _ = iter.count();
    }

    // Dense accumulator should match iterator
    #[test]
    fn accumulate_dense() {
        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
        let gvar = font.gvar().unwrap();
        let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
        let mut count = 0;
        for tuple in gvar_data.tuples() {
            if !tuple.has_deltas_for_all_points() {
                continue;
            }
            let iter_deltas = tuple
                .deltas()
                .map(|delta| (delta.x_delta, delta.y_delta))
                .collect::<Vec<_>>();
            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()];
            tuple
                .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE)
                .unwrap();
            let accum_deltas = delta_buf
                .iter()
                .map(|delta| (delta.x.to_i32(), delta.y.to_i32()))
                .collect::<Vec<_>>();
            assert_eq!(iter_deltas, accum_deltas);
            count += iter_deltas.len();
        }
        assert!(count != 0);
    }

    // Sparse accumulator should match iterator
    #[test]
    fn accumulate_sparse() {
        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
        let gvar = font.gvar().unwrap();
        let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
        let mut count = 0;
        for tuple in gvar_data.tuples() {
            if tuple.has_deltas_for_all_points() {
                continue;
            }
            let iter_deltas = tuple.deltas().collect::<Vec<_>>();
            let max_modified_point = iter_deltas
                .iter()
                .max_by_key(|delta| delta.position)
                .unwrap()
                .position as usize;
            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1];
            let mut flags = vec![PointFlags::default(); delta_buf.len()];
            tuple
                .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE)
                .unwrap();
            let mut accum_deltas = vec![];
            for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() {
                if flag.has_marker(PointMarker::HAS_DELTA) {
                    accum_deltas.push(GlyphDelta::new(
                        i as u16,
                        delta.x.to_i32(),
                        delta.y.to_i32(),
                    ));
                }
            }
            assert_eq!(iter_deltas, accum_deltas);
            count += iter_deltas.len();
        }
        assert!(count != 0);
    }

    #[test]
    fn delta_set_index_map_empty_is_identity() {
        let data = BeBuffer::new()
            .push(0u8) // format 0
            .push(EntryFormat::empty())
            .push(0u16); // map_count
        let map = DeltaSetIndexMap::read(data.data().into()).unwrap();
        assert_eq!(
            map.get(0x0001_0002).unwrap(),
            DeltaSetIndex { outer: 1, inner: 2 }
        );
    }
}
