1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::prelude::*;
use crate::series::IsSorted;

pub(crate) fn new_chunks(chunks: &mut Vec<ArrayRef>, other: &[ArrayRef], len: usize) {
    // Replace an empty array.
    if chunks.len() == 1 && len == 0 {
        *chunks = other.to_owned();
    } else {
        for chunk in other {
            if chunk.len() > 0 {
                chunks.push(chunk.clone());
            }
        }
    }
}

pub(super) fn update_sorted_flag_before_append<T>(ca: &mut ChunkedArray<T>, other: &ChunkedArray<T>)
where
    T: PolarsDataType,
    for<'a> T::Physical<'a>: TotalOrd,
{
    // TODO: attempt to maintain sortedness better in case of nulls.

    // If either is empty, copy the sorted flag from the other.
    if ca.is_empty() {
        ca.set_sorted_flag(other.is_sorted_flag());
        return;
    }
    if other.is_empty() {
        return;
    }

    // Both need to be sorted, in the same order, if the order is maintained.
    // TODO: rework sorted flags, ascending and descending are not mutually
    // exclusive for all-equal/all-null arrays.
    let ls = ca.is_sorted_flag();
    let rs = other.is_sorted_flag();
    if ls != rs || ls == IsSorted::Not || rs == IsSorted::Not {
        ca.set_sorted_flag(IsSorted::Not);
        return;
    }

    // Check the order is maintained.
    let still_sorted = {
        // To prevent potential quadratic append behavior we do not find
        // the last non-null element in ca.
        if let Some(left) = ca.last() {
            if let Some(right_idx) = other.first_non_null() {
                let right = other.get(right_idx).unwrap();
                if ca.is_sorted_ascending_flag() {
                    left.tot_le(&right)
                } else {
                    left.tot_ge(&right)
                }
            } else {
                // Right is only nulls, trivially sorted.
                true
            }
        } else {
            // Last element in left is null, pessimistically assume not sorted.
            false
        }
    };
    if !still_sorted {
        ca.set_sorted_flag(IsSorted::Not);
    }
}

impl<T> ChunkedArray<T>
where
    T: PolarsDataType<Structure = Flat>,
    for<'a> T::Physical<'a>: TotalOrd,
{
    /// Append in place. This is done by adding the chunks of `other` to this [`ChunkedArray`].
    ///
    /// See also [`extend`](Self::extend) for appends to the underlying memory
    pub fn append(&mut self, other: &Self) {
        update_sorted_flag_before_append::<T>(self, other);
        let len = self.len();
        self.length += other.length;
        self.null_count += other.null_count;
        new_chunks(&mut self.chunks, &other.chunks, len);
    }
}

#[doc(hidden)]
impl ListChunked {
    pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
        let dtype = merge_dtypes(self.dtype(), other.dtype())?;
        self.field = Arc::new(Field::new(self.name(), dtype));

        let len = self.len();
        self.length += other.length;
        self.null_count += other.null_count;
        new_chunks(&mut self.chunks, &other.chunks, len);
        self.set_sorted_flag(IsSorted::Not);
        if !other._can_fast_explode() {
            self.unset_fast_explode()
        }
        Ok(())
    }
}

#[cfg(feature = "dtype-array")]
#[doc(hidden)]
impl ArrayChunked {
    pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
        let dtype = merge_dtypes(self.dtype(), other.dtype())?;
        self.field = Arc::new(Field::new(self.name(), dtype));

        let len = self.len();
        self.length += other.length;
        self.null_count += other.null_count;
        new_chunks(&mut self.chunks, &other.chunks, len);
        self.set_sorted_flag(IsSorted::Not);
        Ok(())
    }
}

#[cfg(feature = "object")]
#[doc(hidden)]
impl<T: PolarsObject> ObjectChunked<T> {
    pub fn append(&mut self, other: &Self) {
        let len = self.len();
        self.length += other.length;
        self.null_count += other.null_count;
        self.set_sorted_flag(IsSorted::Not);
        new_chunks(&mut self.chunks, &other.chunks, len);
    }
}