<commit_before>//! Lighting filters and light nodes.

use float_cmp::approx_eq;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Vector2, Vector3};
use num_traits::identities::Zero;
use rayon::prelude::*;
use std::cmp::max;

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{set_attribute, Draw, Element, ElementResult, SetAttributes};
use crate::filters::{
    bounds::BoundsBuilder,
    context::{FilterContext, FilterOutput},
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::paint_server::resolve_color;
use crate::parsers::{NonNegative, NumberOptionalNumber, ParseValue};
use crate::properties::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::session::Session;
use crate::surface_utils::{
    shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
    ImageSurfaceDataExt, Pixel,
};
use crate::transform::Transform;
use crate::unit_interval::UnitInterval;
use crate::util::clamp;
use crate::xml::Attributes;

/// The `feDiffuseLighting` filter primitives.
#[derive(Default)]
pub struct FeDiffuseLighting {
    base: Primitive,
    params: DiffuseLightingParams,
}

#[derive(Clone)]
pub struct DiffuseLightingParams {
    in1: Input,
    surface_scale: f64,
    kernel_unit_length: Option<(f64, f64)>,
    diffuse_constant: f64,
}

impl Default for DiffuseLightingParams {
    fn default() -> Self {
        Self {
            in1: Default::default(),
            surface_scale: 1.0,
            kernel_unit_length: None,
            diffuse_constant: 1.0,
        }
    }
}

/// The `feSpecularLighting` filter primitives.
#[derive(Default)]
pub struct FeSpecularLighting {
    base: Primitive,
    params: SpecularLightingParams,
}

#[derive(Clone)]
pub struct SpecularLightingParams {
    in1: Input,
    surface_scale: f64,
    kernel_unit_length: Option<(f64, f64)>,
    specular_constant: f64,
    specular_exponent: f64,
}

impl Default for SpecularLightingParams {
    fn default() -> Self {
        Self {
            in1: Default::default(),
            surface_scale: 1.0,
            kernel_unit_length: None,
            specular_constant: 1.0,
            specular_exponent: 1.0,
        }
    }
}

/// Resolved `feDiffuseLighting` primitive for rendering.
pub struct DiffuseLighting {
    params: DiffuseLightingParams,
    light: Light,
}

/// Resolved `feSpecularLighting` primitive for rendering.
pub struct SpecularLighting {
    params: SpecularLightingParams,
    light: Light,
}

/// A light source before applying affine transformations, straight out of the SVG.
#[derive(Debug, PartialEq)]
enum UntransformedLightSource {
    Distant(FeDistantLight),
    Point(FePointLight),
    Spot(FeSpotLight),
}

/// A light source with affine transformations applied.
enum LightSource {
    Distant {
        azimuth: f64,
        elevation: f64,
    },
    Point {
        origin: Vector3<f64>,
    },
    Spot {
        origin: Vector3<f64>,
        direction: Vector3<f64>,
        specular_exponent: f64,
        limiting_cone_angle: Option<f64>,
    },
}

impl UntransformedLightSource {
    fn transform(&self, paffine: Transform) -> LightSource {
        match *self {
            UntransformedLightSource::Distant(ref l) => l.transform(),
            UntransformedLightSource::Point(ref l) => l.transform(paffine),
            UntransformedLightSource::Spot(ref l) => l.transform(paffine),
        }
    }
}

struct Light {
    source: UntransformedLightSource,
    lighting_color: cssparser::RGBA,
    color_interpolation_filters: ColorInterpolationFilters,
}

impl Light {
    /// Returns the color and unit (or null) vector from the image sample to the light.
    #[inline]
    pub fn color_and_vector(
        &self,
        source: &LightSource,
        x: f64,
        y: f64,
        z: f64,
    ) -> (cssparser::RGBA, Vector3<f64>) {
        let vector = match *source {
            LightSource::Distant { azimuth, elevation } => {
                let azimuth = azimuth.to_radians();
                let elevation = elevation.to_radians();
                Vector3::new(
                    azimuth.cos() * elevation.cos(),
                    azimuth.sin() * elevation.cos(),
                    elevation.sin(),
                )
            }
            LightSource::Point { origin } | LightSource::Spot { origin, .. } => {
                let mut v = origin - Vector3::new(x, y, z);
                let _ = v.try_normalize_mut(0.0);
                v
            }
        };

        let color = match *source {
            LightSource::Spot {
                direction,
                specular_exponent,
                limiting_cone_angle,
                ..
            } => {
                let minus_l_dot_s = -vector.dot(&direction);
                match limiting_cone_angle {
                    _ if minus_l_dot_s <= 0.0 => cssparser::RGBA::transparent(),
                    Some(a) if minus_l_dot_s < a.to_radians().cos() => {
                        cssparser::RGBA::transparent()
                    }
                    _ => {
                        let factor = minus_l_dot_s.powf(specular_exponent);
                        let compute = |x| (clamp(f64::from(x) * factor, 0.0, 255.0) + 0.5) as u8;

                        cssparser::RGBA {
                            red: compute(self.lighting_color.red),
                            green: compute(self.lighting_color.green),
                            blue: compute(self.lighting_color.blue),
                            alpha: 255,
                        }
                    }
                }
            }
            _ => self.lighting_color,
        };

        (color, vector)
    }
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct FeDistantLight {
    azimuth: f64,
    elevation: f64,
}

impl FeDistantLight {
    fn transform(&self) -> LightSource {
        LightSource::Distant {
            azimuth: self.azimuth,
            elevation: self.elevation,
        }
    }
}

impl SetAttributes for FeDistantLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "azimuth") => {
                    set_attribute(&mut self.azimuth, attr.parse(value), session)
                }
                expanded_name!("", "elevation") => {
                    set_attribute(&mut self.elevation, attr.parse(value), session)
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FeDistantLight {}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct FePointLight {
    x: f64,
    y: f64,
    z: f64,
}

impl FePointLight {
    fn transform(&self, paffine: Transform) -> LightSource {
        let (x, y) = paffine.transform_point(self.x, self.y);
        let z = transform_dist(paffine, self.z);

        LightSource::Point {
            origin: Vector3::new(x, y, z),
        }
    }
}

impl SetAttributes for FePointLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FePointLight {}

#[derive(Clone, Debug, PartialEq)]
pub struct FeSpotLight {
    x: f64,
    y: f64,
    z: f64,
    points_at_x: f64,
    points_at_y: f64,
    points_at_z: f64,
    specular_exponent: f64,
    limiting_cone_angle: Option<f64>,
}

// We need this because, per the spec, the initial values for all fields are 0.0
// except for specular_exponent, which is 1.
impl Default for FeSpotLight {
    fn default() -> FeSpotLight {
        FeSpotLight {
            x: 0.0,
            y: 0.0,
            z: 0.0,
            points_at_x: 0.0,
            points_at_y: 0.0,
            points_at_z: 0.0,
            specular_exponent: 1.0,
            limiting_cone_angle: None,
        }
    }
}

impl FeSpotLight {
    fn transform(&self, paffine: Transform) -> LightSource {
        let (x, y) = paffine.transform_point(self.x, self.y);
        let z = transform_dist(paffine, self.z);
        let (points_at_x, points_at_y) =
            paffine.transform_point(self.points_at_x, self.points_at_y);
        let points_at_z = transform_dist(paffine, self.points_at_z);

        let origin = Vector3::new(x, y, z);
        let mut direction = Vector3::new(points_at_x, points_at_y, points_at_z) - origin;
        let _ = direction.try_normalize_mut(0.0);

        LightSource::Spot {
            origin,
            direction,
            specular_exponent: self.specular_exponent,
            limiting_cone_angle: self.limiting_cone_angle,
        }
    }
}

impl SetAttributes for FeSpotLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
                expanded_name!("", "pointsAtX") => {
                    set_attribute(&mut self.points_at_x, attr.parse(value), session)
                }
                expanded_name!("", "pointsAtY") => {
                    set_attribute(&mut self.points_at_y, attr.parse(value), session)
                }
                expanded_name!("", "pointsAtZ") => {
                    set_attribute(&mut self.points_at_z, attr.parse(value), session)
                }

                expanded_name!("", "specularExponent") => {
                    set_attribute(&mut self.specular_exponent, attr.parse(value), session);
                }

                expanded_name!("", "limitingConeAngle") => {
                    set_attribute(&mut self.limiting_cone_angle, attr.parse(value), session);
                }

                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FeSpotLight {}

/// Applies the `primitiveUnits` coordinate transformation to a non-x or y distance.
#[inline]
fn transform_dist(t: Transform, d: f64) -> f64 {
    d * (t.xx.powi(2) + t.yy.powi(2)).sqrt() / std::f64::consts::SQRT_2
}

impl SetAttributes for FeDiffuseLighting {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs, session);

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "surfaceScale") => {
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
                }
                expanded_name!("", "kernelUnitLength") => {
                    let v: Result<NumberOptionalNumber<f64>, _> = attr.parse(value);
                    match v {
                        Ok(NumberOptionalNumber(x, y)) => {
                            self.params.kernel_unit_length = Some((x, y));
                        }

                        Err(e) => {
                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
                        }
                    }
                }
                expanded_name!("", "diffuseConstant") => {
                    let NonNegative(c) = attr.parse(value)?;
                    self.params.diffuse_constant = c;
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl DiffuseLighting {
    #[inline]
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
        let k = if normal.normal.is_zero() {
            // Common case of (0, 0, 1) normal.
            light_vector.z
        } else {
            let mut n = normal
                .normal
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
            n.component_mul_assign(&normal.factor);
            let normal = Vector3::new(n.x, n.y, 1.0);

            normal.dot(&light_vector) / normal.norm()
        };

        self.params.diffuse_constant * k<commit_msg>filters/lighting.rs: use set_attribute() for diffuse_constant<commit_after>//! Lighting filters and light nodes.

use float_cmp::approx_eq;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Vector2, Vector3};
use num_traits::identities::Zero;
use rayon::prelude::*;
use std::cmp::max;

use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{set_attribute, Draw, Element, ElementResult, SetAttributes};
use crate::filters::{
    bounds::BoundsBuilder,
    context::{FilterContext, FilterOutput},
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::paint_server::resolve_color;
use crate::parsers::{NonNegative, NumberOptionalNumber, ParseValue};
use crate::properties::ColorInterpolationFilters;
use crate::rect::IRect;
use crate::session::Session;
use crate::surface_utils::{
    shared_surface::{ExclusiveImageSurface, SharedImageSurface, SurfaceType},
    ImageSurfaceDataExt, Pixel,
};
use crate::transform::Transform;
use crate::unit_interval::UnitInterval;
use crate::util::clamp;
use crate::xml::Attributes;

/// The `feDiffuseLighting` filter primitives.
#[derive(Default)]
pub struct FeDiffuseLighting {
    base: Primitive,
    params: DiffuseLightingParams,
}

#[derive(Clone)]
pub struct DiffuseLightingParams {
    in1: Input,
    surface_scale: f64,
    kernel_unit_length: Option<(f64, f64)>,
    diffuse_constant: NonNegative,
}

impl Default for DiffuseLightingParams {
    fn default() -> Self {
        Self {
            in1: Default::default(),
            surface_scale: 1.0,
            kernel_unit_length: None,
            diffuse_constant: NonNegative(1.0),
        }
    }
}

/// The `feSpecularLighting` filter primitives.
#[derive(Default)]
pub struct FeSpecularLighting {
    base: Primitive,
    params: SpecularLightingParams,
}

#[derive(Clone)]
pub struct SpecularLightingParams {
    in1: Input,
    surface_scale: f64,
    kernel_unit_length: Option<(f64, f64)>,
    specular_constant: f64,
    specular_exponent: f64,
}

impl Default for SpecularLightingParams {
    fn default() -> Self {
        Self {
            in1: Default::default(),
            surface_scale: 1.0,
            kernel_unit_length: None,
            specular_constant: 1.0,
            specular_exponent: 1.0,
        }
    }
}

/// Resolved `feDiffuseLighting` primitive for rendering.
pub struct DiffuseLighting {
    params: DiffuseLightingParams,
    light: Light,
}

/// Resolved `feSpecularLighting` primitive for rendering.
pub struct SpecularLighting {
    params: SpecularLightingParams,
    light: Light,
}

/// A light source before applying affine transformations, straight out of the SVG.
#[derive(Debug, PartialEq)]
enum UntransformedLightSource {
    Distant(FeDistantLight),
    Point(FePointLight),
    Spot(FeSpotLight),
}

/// A light source with affine transformations applied.
enum LightSource {
    Distant {
        azimuth: f64,
        elevation: f64,
    },
    Point {
        origin: Vector3<f64>,
    },
    Spot {
        origin: Vector3<f64>,
        direction: Vector3<f64>,
        specular_exponent: f64,
        limiting_cone_angle: Option<f64>,
    },
}

impl UntransformedLightSource {
    fn transform(&self, paffine: Transform) -> LightSource {
        match *self {
            UntransformedLightSource::Distant(ref l) => l.transform(),
            UntransformedLightSource::Point(ref l) => l.transform(paffine),
            UntransformedLightSource::Spot(ref l) => l.transform(paffine),
        }
    }
}

struct Light {
    source: UntransformedLightSource,
    lighting_color: cssparser::RGBA,
    color_interpolation_filters: ColorInterpolationFilters,
}

impl Light {
    /// Returns the color and unit (or null) vector from the image sample to the light.
    #[inline]
    pub fn color_and_vector(
        &self,
        source: &LightSource,
        x: f64,
        y: f64,
        z: f64,
    ) -> (cssparser::RGBA, Vector3<f64>) {
        let vector = match *source {
            LightSource::Distant { azimuth, elevation } => {
                let azimuth = azimuth.to_radians();
                let elevation = elevation.to_radians();
                Vector3::new(
                    azimuth.cos() * elevation.cos(),
                    azimuth.sin() * elevation.cos(),
                    elevation.sin(),
                )
            }
            LightSource::Point { origin } | LightSource::Spot { origin, .. } => {
                let mut v = origin - Vector3::new(x, y, z);
                let _ = v.try_normalize_mut(0.0);
                v
            }
        };

        let color = match *source {
            LightSource::Spot {
                direction,
                specular_exponent,
                limiting_cone_angle,
                ..
            } => {
                let minus_l_dot_s = -vector.dot(&direction);
                match limiting_cone_angle {
                    _ if minus_l_dot_s <= 0.0 => cssparser::RGBA::transparent(),
                    Some(a) if minus_l_dot_s < a.to_radians().cos() => {
                        cssparser::RGBA::transparent()
                    }
                    _ => {
                        let factor = minus_l_dot_s.powf(specular_exponent);
                        let compute = |x| (clamp(f64::from(x) * factor, 0.0, 255.0) + 0.5) as u8;

                        cssparser::RGBA {
                            red: compute(self.lighting_color.red),
                            green: compute(self.lighting_color.green),
                            blue: compute(self.lighting_color.blue),
                            alpha: 255,
                        }
                    }
                }
            }
            _ => self.lighting_color,
        };

        (color, vector)
    }
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct FeDistantLight {
    azimuth: f64,
    elevation: f64,
}

impl FeDistantLight {
    fn transform(&self) -> LightSource {
        LightSource::Distant {
            azimuth: self.azimuth,
            elevation: self.elevation,
        }
    }
}

impl SetAttributes for FeDistantLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "azimuth") => {
                    set_attribute(&mut self.azimuth, attr.parse(value), session)
                }
                expanded_name!("", "elevation") => {
                    set_attribute(&mut self.elevation, attr.parse(value), session)
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FeDistantLight {}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct FePointLight {
    x: f64,
    y: f64,
    z: f64,
}

impl FePointLight {
    fn transform(&self, paffine: Transform) -> LightSource {
        let (x, y) = paffine.transform_point(self.x, self.y);
        let z = transform_dist(paffine, self.z);

        LightSource::Point {
            origin: Vector3::new(x, y, z),
        }
    }
}

impl SetAttributes for FePointLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FePointLight {}

#[derive(Clone, Debug, PartialEq)]
pub struct FeSpotLight {
    x: f64,
    y: f64,
    z: f64,
    points_at_x: f64,
    points_at_y: f64,
    points_at_z: f64,
    specular_exponent: f64,
    limiting_cone_angle: Option<f64>,
}

// We need this because, per the spec, the initial values for all fields are 0.0
// except for specular_exponent, which is 1.
impl Default for FeSpotLight {
    fn default() -> FeSpotLight {
        FeSpotLight {
            x: 0.0,
            y: 0.0,
            z: 0.0,
            points_at_x: 0.0,
            points_at_y: 0.0,
            points_at_z: 0.0,
            specular_exponent: 1.0,
            limiting_cone_angle: None,
        }
    }
}

impl FeSpotLight {
    fn transform(&self, paffine: Transform) -> LightSource {
        let (x, y) = paffine.transform_point(self.x, self.y);
        let z = transform_dist(paffine, self.z);
        let (points_at_x, points_at_y) =
            paffine.transform_point(self.points_at_x, self.points_at_y);
        let points_at_z = transform_dist(paffine, self.points_at_z);

        let origin = Vector3::new(x, y, z);
        let mut direction = Vector3::new(points_at_x, points_at_y, points_at_z) - origin;
        let _ = direction.try_normalize_mut(0.0);

        LightSource::Spot {
            origin,
            direction,
            specular_exponent: self.specular_exponent,
            limiting_cone_angle: self.limiting_cone_angle,
        }
    }
}

impl SetAttributes for FeSpotLight {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
                expanded_name!("", "z") => set_attribute(&mut self.z, attr.parse(value), session),
                expanded_name!("", "pointsAtX") => {
                    set_attribute(&mut self.points_at_x, attr.parse(value), session)
                }
                expanded_name!("", "pointsAtY") => {
                    set_attribute(&mut self.points_at_y, attr.parse(value), session)
                }
                expanded_name!("", "pointsAtZ") => {
                    set_attribute(&mut self.points_at_z, attr.parse(value), session)
                }

                expanded_name!("", "specularExponent") => {
                    set_attribute(&mut self.specular_exponent, attr.parse(value), session);
                }

                expanded_name!("", "limitingConeAngle") => {
                    set_attribute(&mut self.limiting_cone_angle, attr.parse(value), session);
                }

                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for FeSpotLight {}

/// Applies the `primitiveUnits` coordinate transformation to a non-x or y distance.
#[inline]
fn transform_dist(t: Transform, d: f64) -> f64 {
    d * (t.xx.powi(2) + t.yy.powi(2)).sqrt() / std::f64::consts::SQRT_2
}

impl SetAttributes for FeDiffuseLighting {
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) -> ElementResult {
        self.params.in1 = self.base.parse_one_input(attrs, session);

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "surfaceScale") => {
                    set_attribute(&mut self.params.surface_scale, attr.parse(value), session);
                }
                expanded_name!("", "kernelUnitLength") => {
                    let v: Result<NumberOptionalNumber<f64>, _> = attr.parse(value);
                    match v {
                        Ok(NumberOptionalNumber(x, y)) => {
                            self.params.kernel_unit_length = Some((x, y));
                        }

                        Err(e) => {
                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
                        }
                    }
                }
                expanded_name!("", "diffuseConstant") => {
                    set_attribute(
                        &mut self.params.diffuse_constant,
                        attr.parse(value),
                        session,
                    );
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl DiffuseLighting {
    #[inline]
    fn compute_factor(&self, normal: Normal, light_vector: Vector3<f64>) -> f64 {
        let k = if normal.normal.is_zero() {
            // Common case of (0, 0, 1) normal.
            light_vector.z
        } else {
            let mut n = normal
                .normal
                .map(|x| f64::from(x) * self.params.surface_scale / 255.);
            n.component_mul_assign(&normal.factor);
            let normal = Vector3::new(n.x, n.y, 1.0);

            normal.dot(&light_vector) / normal.norm()
        };

        self.params.diffuse_constant.0 * k
{'fraction_non_alphanumeric': 0.12138254247217341, 'fraction_numerical': 0.012224174965826987, 'mean_word_length': 1.986122448979592, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2097022', 'n_tokens_mistral': 7890, 'n_tokens_neox': 7567, 'n_words': 1617}