// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub struct SparseVarRegionAxis {
axis_index : Int
axis : VarRegionAxis
} derive(Eq, Show, ToJson)
///|
pub struct SparseVariationRegion {
axes : Array[SparseVarRegionAxis]
} derive(Eq, Show, ToJson)
///|
pub struct SparseVarRegionList {
regions : Array[SparseVariationRegion]
} derive(Eq, Show, ToJson)
///|
pub struct MultiVarData {
region_indices : Array[Int]
delta_sets : CffIndex
} derive(Show, ToJson)
///|
pub struct MultiItemVariationStore {
region_list : SparseVarRegionList
data_sets : Array[MultiVarData]
} derive(Show, ToJson)
fn SparseVariationRegion::evaluate(self : SparseVariationRegion, coords : ArrayView[Int]) -> Double {
let mut scalar = 1.0
for axis in self.axes {
let coord = if axis.axis_index >= 0 && axis.axis_index < coords.length() {
coords[axis.axis_index]
} else {
0
}
let factor = axis.axis.evaluate(coord)
if factor == 0.0 {
return 0.0
}
scalar = scalar * factor
}
scalar
}
fn SparseVarRegionList::evaluate(
self : SparseVarRegionList,
region_index : Int,
coords : ArrayView[Int],
) -> Double {
if region_index < 0 || region_index >= self.regions.length() {
return 0.0
}
self.regions[region_index].evaluate(coords)
}
fn parse_sparse_variation_region(
data : BytesView,
offset : Int,
) -> Result[SparseVariationRegion, VarError] {
let count = read_u16_int(data, offset)
match count {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let base = offset + 2
let record_size = 8
let total = count * record_size
if base < 0 || base + total > data.length() {
return Err(UnexpectedEof)
}
let axes : Array[SparseVarRegionAxis] = []
let mut pos = base
for _ in 0.. return Err(err)
(_, Err(err), _, _) => return Err(err)
(_, _, Err(err), _) => return Err(err)
(_, _, _, Err(err)) => return Err(err)
(Ok(axis_index), Ok(start_coord), Ok(peak_coord), Ok(end_coord)) => {
let axis = VarRegionAxis::{
start_coord,
peak_coord,
end_coord,
}
axes.push(SparseVarRegionAxis::{ axis_index, axis })
}
}
pos = pos + record_size
}
Ok(SparseVariationRegion::{ axes })
}
}
}
fn parse_sparse_var_region_list(
data : BytesView,
offset : Int,
) -> Result[SparseVarRegionList, VarError] {
let count = read_u16_int(data, offset)
match count {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let offsets_base = offset + 2
let total = count * 4
if offsets_base < 0 || offsets_base + total > data.length() {
return Err(UnexpectedEof)
}
let regions : Array[SparseVariationRegion] = []
for i in 0.. return Err(err)
Ok(entry_offset) => {
let region_offset = offset + entry_offset
if region_offset < 0 || region_offset >= data.length() {
return Err(UnexpectedEof)
}
let region = match parse_sparse_variation_region(data, region_offset) {
Err(err) => return Err(err)
Ok(value) => value
}
regions.push(region)
}
}
}
Ok(SparseVarRegionList::{ regions })
}
}
}
fn parse_multi_var_data(data : BytesView, offset : Int) -> Result[MultiVarData, VarError] {
let format = read_u8_int(data, offset)
match format {
Err(err) => Err(err)
Ok(format) => {
if format != 1 {
return Err(InvalidFormat)
}
let count = read_u16_int(data, offset + 1)
match count {
Err(err) => Err(err)
Ok(count) => {
if count < 0 {
return Err(InvalidFormat)
}
let indices_base = offset + 3
let total = count * 2
if indices_base < 0 || indices_base + total > data.length() {
return Err(UnexpectedEof)
}
let region_indices : Array[Int] = []
let mut pos = indices_base
for _ in 0.. return Err(err)
Ok(v) => region_indices.push(v)
}
pos = pos + 2
}
let delta_sets = match parse_cff2_index(data, pos) {
Err(err) => return Err(err)
Ok(value) => value
}
Ok(MultiVarData::{ region_indices, delta_sets })
}
}
}
}
}
fn MultiVarData::add_delta(
self : MultiVarData,
inner : Int,
coords : ArrayView[Int],
regions : SparseVarRegionList,
out : Array[Double],
) -> Unit {
if inner < 0 || inner >= self.delta_sets.count {
return
}
let bytes = match self.delta_sets.item_bytes(inner) {
Err(_) => return
Ok(value) => value
}
let values = match decode_tuple_values(bytes[:]) {
Err(_) => return
Ok(value) => value
}
let out_len = out.length()
let region_count = self.region_indices.length()
let mut index = 0
for region_index in 0.. Result[MultiItemVariationStore, VarError] {
let format = read_u16_int(data, 0)
let region_offset = read_u32_int(data, 2)
let data_count = read_u16_int(data, 6)
match (format, region_offset, data_count) {
(Err(err), _, _) => Err(err)
(_, Err(err), _) => Err(err)
(_, _, Err(err)) => Err(err)
(Ok(format), Ok(region_offset), Ok(data_count)) => {
if format != 1 {
return Err(InvalidFormat)
}
if region_offset < 0 || region_offset >= data.length() {
return Err(UnexpectedEof)
}
let region_list = match parse_sparse_var_region_list(data, region_offset) {
Err(err) => return Err(err)
Ok(value) => value
}
let data_sets : Array[MultiVarData] = []
let mut pos = 8
for _ in 0.. return Err(err)
Ok(entry_offset) => {
if entry_offset < 0 || entry_offset >= data.length() {
return Err(UnexpectedEof)
}
let data_set = match parse_multi_var_data(data, entry_offset) {
Err(err) => return Err(err)
Ok(value) => value
}
data_sets.push(data_set)
}
}
pos = pos + 4
}
Ok(MultiItemVariationStore::{ region_list, data_sets })
}
}
}
///|
pub fn MultiItemVariationStore::get_delta(
self : MultiItemVariationStore,
var_idx : UInt,
coords : Array[Int],
out : Array[Double],
) -> Unit {
let outer = ((var_idx >> 16) & 0xFFFFU).reinterpret_as_int()
let inner = (var_idx & 0xFFFFU).reinterpret_as_int()
if outer < 0 || outer >= self.data_sets.length() {
return
}
self.data_sets[outer].add_delta(inner, coords[:], self.region_list, out)
}