Repository: pola-rs/polars
File: crates/polars-core/src/series/implementations/categorical.rs (Rust)

Code before:
            }
            fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
                invalid_operation_panic!(into_total_eq_inner, self)
            }

            fn vec_hash(
                &self,
                random_state: PlSeedableRandomStateQuality,
                buf: &mut Vec<u64>,
            ) -> PolarsResult<()> {
                self.0.physical().vec_hash(random_state, buf)?;
                Ok(())
            }

            fn vec_hash_combine(
                &self,
                build_hasher: PlSeedableRandomStateQuality,
                hashes: &mut [u64],
            ) -> PolarsResult<()> {
                self.0.physical().vec_hash_combine(build_hasher, hashes)?;
                Ok(())
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
                self.apply_on_phys(|phys| phys.agg_min(groups).$ca_fn().unwrap().clone())
                    .into_series()
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
                self.apply_on_phys(|phys| phys.agg_max(groups).$ca_fn().unwrap().clone())
                    .into_series()
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
                // we cannot cast and dispatch as the inner type of the list would be incorrect
                let list = self.0.physical().agg_list(groups);
                let mut list = list.list().unwrap().clone();
                unsafe { list.to_logical(self.dtype().clone()) };
                list.into_series()
            }

            #[cfg(feature = "algorithm_group_by")]
            fn group_tuples(&self, multithreaded: bool, sorted: bool) -> PolarsResult<GroupsType> {
                self.0.physical().group_tuples(multithreaded, sorted)
            }

            fn arg_sort_multiple(
                &self,

Review comment (bug):
This seems incorrect - categoricals must return a min/max following lexical ordering rather than physical - you can see this here:

```python
print(
    pl.Series("cat", ["B", "A"], dtype=pl.Categorical(pl.Categories.random()))
    .to_frame()
    .with_columns(
        cat_phys=pl.col("cat").to_physical(),
        cat_min=pl.col("cat").min(),
        cat_min_by_phys=pl.col("cat").get(pl.col("cat").to_physical().arg_min()),
    )
)
# shape: (2, 4)
# ┌─────┬──────────┬─────────┬─────────────────┐
# │ cat ┆ cat_phys ┆ cat_min ┆ cat_min_by_phys │
# │ --- ┆ ---      ┆ ---     ┆ ---             │
# │ cat ┆ u32      ┆ cat     ┆ cat             │
# ╞═════╪══════════╪═════════╪═════════════════╡
# │ B   ┆ 0        ┆ A       ┆ B               │
# │ A   ┆ 1        ┆ A       ┆ B               │
# └─────┴──────────┴─────────┴─────────────────┘
```


Code after:
            }
            fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
                invalid_operation_panic!(into_total_eq_inner, self)
            }

            fn vec_hash(
                &self,
                random_state: PlSeedableRandomStateQuality,
                buf: &mut Vec<u64>,
            ) -> PolarsResult<()> {
                self.0.physical().vec_hash(random_state, buf)?;
                Ok(())
            }

            fn vec_hash_combine(
                &self,
                build_hasher: PlSeedableRandomStateQuality,
                hashes: &mut [u64],
            ) -> PolarsResult<()> {
                self.0.physical().vec_hash_combine(build_hasher, hashes)?;
                Ok(())
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_min(&self, groups: &GroupsType) -> Series {
                if self.0.uses_lexical_ordering() {
                    unimplemented!()
                } else {
                    self.apply_on_phys(|phys| phys.agg_min(groups).$ca_fn().unwrap().clone())
                        .into_series()
                }
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_max(&self, groups: &GroupsType) -> Series {
                if self.0.uses_lexical_ordering() {
                    unimplemented!()
                } else {
                    self.apply_on_phys(|phys| phys.agg_max(groups).$ca_fn().unwrap().clone())
                        .into_series()
                }
            }

            #[cfg(feature = "algorithm_group_by")]
            unsafe fn agg_list(&self, groups: &GroupsType) -> Series {
                // we cannot cast and dispatch as the inner type of the list would be incorrect
                let list = self.0.physical().agg_list(groups);
                let mut list = list.list().unwrap().clone();
                unsafe { list.to_logical(self.dtype().clone()) };
                list.into_series()
            }