ndarray/
impl_constructors.rs

1// Copyright 2014-2016 bluss and ndarray developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Constructor methods for ndarray
10//!
11//!
12
13#![allow(clippy::match_wild_err_arm)]
14use alloc::vec;
15#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17#[cfg(feature = "std")]
18use num_traits::Float;
19use num_traits::{One, Zero};
20use std::mem;
21use std::mem::MaybeUninit;
22
23use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr;
24use crate::dimension::{self, CanIndexCheckMode};
25use crate::error::{self, ShapeError};
26use crate::extension::nonnull::nonnull_from_vec_data;
27use crate::imp_prelude::*;
28use crate::indexes;
29use crate::indices;
30#[cfg(feature = "std")]
31use crate::iterators::to_vec;
32use crate::iterators::to_vec_mapped;
33use crate::iterators::TrustedIterator;
34use crate::StrideShape;
35#[cfg(feature = "std")]
36use crate::{geomspace, linspace, logspace};
37#[allow(unused_imports)]
38use rawpointer::PointerExt;
39
40/// # Constructor Methods for Owned Arrays
41///
42/// Note that the constructor methods apply to `Array` and `ArcArray`,
43/// the two array types that have owned storage.
44///
45/// ## Constructor methods for one-dimensional arrays.
46impl<S, A> ArrayBase<S, Ix1>
47where S: DataOwned<Elem = A>
48{
49    /// Create a one-dimensional array from a vector (no copying needed).
50    ///
51    /// **Panics** if the length is greater than `isize::MAX`.
52    ///
53    /// ```rust
54    /// use ndarray::Array;
55    ///
56    /// let array = Array::from_vec(vec![1., 2., 3., 4.]);
57    /// ```
58    pub fn from_vec(v: Vec<A>) -> Self
59    {
60        if mem::size_of::<A>() == 0 {
61            assert!(
62                v.len() <= isize::MAX as usize,
63                "Length must fit in `isize`.",
64            );
65        }
66        unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) }
67    }
68
69    /// Create a one-dimensional array from an iterator or iterable.
70    ///
71    /// **Panics** if the length is greater than `isize::MAX`.
72    ///
73    /// ```rust
74    /// use ndarray::Array;
75    ///
76    /// let array = Array::from_iter(0..10);
77    /// ```
78    #[allow(clippy::should_implement_trait)]
79    pub fn from_iter<I: IntoIterator<Item = A>>(iterable: I) -> Self
80    {
81        Self::from_vec(iterable.into_iter().collect())
82    }
83
84    /// Create a one-dimensional array with `n` evenly spaced elements from
85    /// `start` to `end` (inclusive). `A` must be a floating point type.
86    ///
87    /// Note that if `start > end`, the first element will still be `start`,
88    /// and the following elements will be decreasing. This is different from
89    /// the behavior of `std::ops::RangeInclusive`, which interprets `start >
90    /// end` to mean that the range is empty.
91    ///
92    /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1`
93    /// to type `A` fails.
94    ///
95    /// ```rust
96    /// use ndarray::{Array, arr1};
97    ///
98    /// let array = Array::linspace(0., 1., 5);
99    /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0]))
100    /// ```
101    #[cfg(feature = "std")]
102    pub fn linspace(start: A, end: A, n: usize) -> Self
103    where A: Float
104    {
105        Self::from(to_vec(linspace::linspace(start, end, n)))
106    }
107
108    /// Create a one-dimensional array with elements from `start` to `end`
109    /// (exclusive), incrementing by `step`. `A` must be a floating point type.
110    ///
111    /// **Panics** if the length is greater than `isize::MAX`.
112    ///
113    /// ```rust
114    /// use ndarray::{Array, arr1};
115    ///
116    /// let array = Array::range(0., 5., 1.);
117    /// assert!(array == arr1(&[0., 1., 2., 3., 4.]))
118    /// ```
119    #[cfg(feature = "std")]
120    pub fn range(start: A, end: A, step: A) -> Self
121    where A: Float
122    {
123        Self::from(to_vec(linspace::range(start, end, step)))
124    }
125
126    /// Create a one-dimensional array with `n` logarithmically spaced
127    /// elements, with the starting value being `base.powf(start)` and the
128    /// final one being `base.powf(end)`. `A` must be a floating point type.
129    ///
130    /// If `base` is negative, all values will be negative.
131    ///
132    /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1`
133    /// to type `A` fails.
134    ///
135    /// ```rust
136    /// # #[cfg(feature = "approx")] {
137    /// use approx::assert_abs_diff_eq;
138    /// use ndarray::{Array, arr1};
139    ///
140    /// let array = Array::logspace(10.0, 0.0, 3.0, 4);
141    /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]));
142    ///
143    /// let array = Array::logspace(-10.0, 3.0, 0.0, 4);
144    /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]));
145    /// # }
146    /// ```
147    #[cfg(feature = "std")]
148    pub fn logspace(base: A, start: A, end: A, n: usize) -> Self
149    where A: Float
150    {
151        Self::from(to_vec(logspace::logspace(base, start, end, n)))
152    }
153
154    /// Create a one-dimensional array with `n` geometrically spaced elements
155    /// from `start` to `end` (inclusive). `A` must be a floating point type.
156    ///
157    /// Returns `None` if `start` and `end` have different signs or if either
158    /// one is zero. Conceptually, this means that in order to obtain a `Some`
159    /// result, `end / start` must be positive.
160    ///
161    /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1`
162    /// to type `A` fails.
163    ///
164    /// ```rust
165    /// # fn example() -> Option<()> {
166    /// # #[cfg(feature = "approx")] {
167    /// use approx::assert_abs_diff_eq;
168    /// use ndarray::{Array, arr1};
169    ///
170    /// let array = Array::geomspace(1e0, 1e3, 4)?;
171    /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]), epsilon = 1e-12);
172    ///
173    /// let array = Array::geomspace(-1e3, -1e0, 4)?;
174    /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]), epsilon = 1e-12);
175    /// # }
176    /// # Some(())
177    /// # }
178    /// #
179    /// # example().unwrap();
180    /// ```
181    #[cfg(feature = "std")]
182    pub fn geomspace(start: A, end: A, n: usize) -> Option<Self>
183    where A: Float
184    {
185        Some(Self::from(to_vec(geomspace::geomspace(start, end, n)?)))
186    }
187}
188
189/// ## Constructor methods for two-dimensional arrays.
190impl<S, A> ArrayBase<S, Ix2>
191where S: DataOwned<Elem = A>
192{
193    /// Create an identity matrix of size `n` (square 2D array).
194    ///
195    /// **Panics** if `n * n` would overflow `isize`.
196    pub fn eye(n: Ix) -> Self
197    where
198        S: DataMut,
199        A: Clone + Zero + One,
200    {
201        let mut eye = Self::zeros((n, n));
202        for a_ii in eye.diag_mut() {
203            *a_ii = A::one();
204        }
205        eye
206    }
207
208    /// Create a 2D matrix from its diagonal
209    ///
210    /// **Panics** if `diag.len() * diag.len()` would overflow `isize`.
211    ///
212    /// ```rust
213    /// use ndarray::{Array2, arr1, arr2};
214    ///
215    /// let diag = arr1(&[1, 2]);
216    /// let array = Array2::from_diag(&diag);
217    /// assert_eq!(array, arr2(&[[1, 0], [0, 2]]));
218    /// ```
219    pub fn from_diag<S2>(diag: &ArrayBase<S2, Ix1>) -> Self
220    where
221        A: Clone + Zero,
222        S: DataMut,
223        S2: Data<Elem = A>,
224    {
225        let n = diag.len();
226        let mut arr = Self::zeros((n, n));
227        arr.diag_mut().assign(diag);
228        arr
229    }
230
231    /// Create a square 2D matrix of the specified size, with the specified
232    /// element along the diagonal and zeros elsewhere.
233    ///
234    /// **Panics** if `n * n` would overflow `isize`.
235    ///
236    /// ```rust
237    /// use ndarray::{array, Array2};
238    ///
239    /// let array = Array2::from_diag_elem(2, 5.);
240    /// assert_eq!(array, array![[5., 0.], [0., 5.]]);
241    /// ```
242    pub fn from_diag_elem(n: usize, elem: A) -> Self
243    where
244        S: DataMut,
245        A: Clone + Zero,
246    {
247        let mut eye = Self::zeros((n, n));
248        for a_ii in eye.diag_mut() {
249            *a_ii = elem.clone();
250        }
251        eye
252    }
253}
254
255#[cfg(not(debug_assertions))]
256#[allow(clippy::match_wild_err_arm)]
257macro_rules! size_of_shape_checked_unwrap {
258    ($dim:expr) => {
259        match dimension::size_of_shape_checked($dim) {
260            Ok(sz) => sz,
261            Err(_) => {
262                panic!("ndarray: Shape too large, product of non-zero axis lengths overflows isize")
263            }
264        }
265    };
266}
267
268#[cfg(debug_assertions)]
269macro_rules! size_of_shape_checked_unwrap {
270    ($dim:expr) => {
271        match dimension::size_of_shape_checked($dim) {
272            Ok(sz) => sz,
273            Err(_) => panic!(
274                "ndarray: Shape too large, product of non-zero axis lengths \
275                 overflows isize in shape {:?}",
276                $dim
277            ),
278        }
279    };
280}
281
282/// ## Constructor methods for n-dimensional arrays.
283///
284/// The `shape` argument can be an integer or a tuple of integers to specify
285/// a static size. For example `10` makes a length 10 one-dimensional array
286/// (dimension type `Ix1`) and `(5, 6)` a 5 × 6 array (dimension type `Ix2`).
287///
288/// With the trait `ShapeBuilder` in scope, there is the method `.f()` to select
289/// column major (“f” order) memory layout instead of the default row major.
290/// For example `Array::zeros((5, 6).f())` makes a column major 5 × 6 array.
291///
292/// Use [`type@IxDyn`] for the shape to create an array with dynamic
293/// number of axes.
294///
295/// Finally, the few constructors that take a completely general
296/// `Into<StrideShape>` argument *optionally* support custom strides, for
297/// example a shape given like `(10, 2, 2).strides((1, 10, 20))` is valid.
298impl<S, A, D> ArrayBase<S, D>
299where
300    S: DataOwned<Elem = A>,
301    D: Dimension,
302{
303    /// Create an array with copies of `elem`, shape `shape`.
304    ///
305    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
306    ///
307    /// ```
308    /// use ndarray::{Array, arr3, ShapeBuilder};
309    ///
310    /// let a = Array::from_elem((2, 2, 2), 1.);
311    ///
312    /// assert!(
313    ///     a == arr3(&[[[1., 1.],
314    ///                  [1., 1.]],
315    ///                 [[1., 1.],
316    ///                  [1., 1.]]])
317    /// );
318    /// assert!(a.strides() == &[4, 2, 1]);
319    ///
320    /// let b = Array::from_elem((2, 2, 2).f(), 1.);
321    /// assert!(b.strides() == &[1, 2, 4]);
322    /// ```
323    pub fn from_elem<Sh>(shape: Sh, elem: A) -> Self
324    where
325        A: Clone,
326        Sh: ShapeBuilder<Dim = D>,
327    {
328        let shape = shape.into_shape_with_order();
329        let size = size_of_shape_checked_unwrap!(&shape.dim);
330        let v = vec![elem; size];
331        unsafe { Self::from_shape_vec_unchecked(shape, v) }
332    }
333
334    /// Create an array with zeros, shape `shape`.
335    ///
336    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
337    pub fn zeros<Sh>(shape: Sh) -> Self
338    where
339        A: Clone + Zero,
340        Sh: ShapeBuilder<Dim = D>,
341    {
342        Self::from_elem(shape, A::zero())
343    }
344
345    /// Create an array with ones, shape `shape`.
346    ///
347    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
348    pub fn ones<Sh>(shape: Sh) -> Self
349    where
350        A: Clone + One,
351        Sh: ShapeBuilder<Dim = D>,
352    {
353        Self::from_elem(shape, A::one())
354    }
355
356    /// Create an array with default values, shape `shape`
357    ///
358    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
359    pub fn default<Sh>(shape: Sh) -> Self
360    where
361        A: Default,
362        Sh: ShapeBuilder<Dim = D>,
363    {
364        Self::from_shape_simple_fn(shape, A::default)
365    }
366
367    /// Create an array with values created by the function `f`.
368    ///
369    /// `f` is called with no argument, and it should return the element to
370    /// create. If the precise index of the element to create is needed,
371    /// use [`from_shape_fn`](ArrayBase::from_shape_fn) instead.
372    ///
373    /// This constructor can be useful if the element order is not important,
374    /// for example if they are identical or random.
375    ///
376    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
377    pub fn from_shape_simple_fn<Sh, F>(shape: Sh, mut f: F) -> Self
378    where
379        Sh: ShapeBuilder<Dim = D>,
380        F: FnMut() -> A,
381    {
382        let shape = shape.into_shape_with_order();
383        let len = size_of_shape_checked_unwrap!(&shape.dim);
384        let v = to_vec_mapped(0..len, move |_| f());
385        unsafe { Self::from_shape_vec_unchecked(shape, v) }
386    }
387
388    /// Create an array with values created by the function `f`.
389    ///
390    /// `f` is called with the index of the element to create; the elements are
391    /// visited in arbitrary order.
392    ///
393    /// **Panics** if the product of non-zero axis lengths overflows `isize`.
394    ///
395    /// ```
396    /// use ndarray::{Array, arr2};
397    ///
398    /// // Create a table of i × j (with i and j from 1 to 3)
399    /// let ij_table = Array::from_shape_fn((3, 3), |(i, j)| (1 + i) * (1 + j));
400    ///
401    /// assert_eq!(
402    ///     ij_table,
403    ///     arr2(&[[1, 2, 3],
404    ///            [2, 4, 6],
405    ///            [3, 6, 9]])
406    /// );
407    /// ```
408    pub fn from_shape_fn<Sh, F>(shape: Sh, f: F) -> Self
409    where
410        Sh: ShapeBuilder<Dim = D>,
411        F: FnMut(D::Pattern) -> A,
412    {
413        let shape = shape.into_shape_with_order();
414        let _ = size_of_shape_checked_unwrap!(&shape.dim);
415        if shape.is_c() {
416            let v = to_vec_mapped(indices(shape.dim.clone()).into_iter(), f);
417            unsafe { Self::from_shape_vec_unchecked(shape, v) }
418        } else {
419            let dim = shape.dim.clone();
420            let v = to_vec_mapped(indexes::indices_iter_f(dim), f);
421            unsafe { Self::from_shape_vec_unchecked(shape, v) }
422        }
423    }
424
425    /// Create an array with the given shape from a vector. (No cloning of
426    /// elements needed.)
427    ///
428    /// ----
429    ///
430    /// For a contiguous c- or f-order shape, the following applies:
431    ///
432    /// **Errors** if `shape` does not correspond to the number of elements in
433    /// `v` or if the shape/strides would result in overflowing `isize`.
434    ///
435    /// ----
436    ///
437    /// For custom strides, the following applies:
438    ///
439    /// **Errors** if strides and dimensions can point out of bounds of `v`, if
440    /// strides allow multiple indices to point to the same element, or if the
441    /// shape/strides would result in overflowing `isize`.
442    ///
443    /// ```
444    /// use ndarray::Array;
445    /// use ndarray::ShapeBuilder; // Needed for .strides() method
446    /// use ndarray::arr2;
447    ///
448    /// let a = Array::from_shape_vec((2, 2), vec![1., 2., 3., 4.]);
449    /// assert!(a.is_ok());
450    ///
451    /// let b = Array::from_shape_vec((2, 2).strides((1, 2)),
452    ///                               vec![1., 2., 3., 4.]).unwrap();
453    /// assert!(
454    ///     b == arr2(&[[1., 3.],
455    ///                 [2., 4.]])
456    /// );
457    /// ```
458    pub fn from_shape_vec<Sh>(shape: Sh, v: Vec<A>) -> Result<Self, ShapeError>
459    where Sh: Into<StrideShape<D>>
460    {
461        // eliminate the type parameter Sh as soon as possible
462        Self::from_shape_vec_impl(shape.into(), v)
463    }
464
465    fn from_shape_vec_impl(shape: StrideShape<D>, v: Vec<A>) -> Result<Self, ShapeError>
466    {
467        let dim = shape.dim;
468        let is_custom = shape.strides.is_custom();
469        dimension::can_index_slice_with_strides(&v, &dim, &shape.strides, dimension::CanIndexCheckMode::OwnedMutable)?;
470        if !is_custom && dim.size() != v.len() {
471            return Err(error::incompatible_shapes(&Ix1(v.len()), &dim));
472        }
473        let strides = shape.strides.strides_for_dim(&dim);
474        unsafe { Ok(Self::from_vec_dim_stride_unchecked(dim, strides, v)) }
475    }
476
477    /// Creates an array from a vector and interpret it according to the
478    /// provided shape and strides. (No cloning of elements needed.)
479    ///
480    /// # Safety
481    ///
482    /// The caller must ensure that the following conditions are met:
483    ///
484    /// 1. The ndim of `dim` and `strides` must be the same.
485    ///
486    /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`.
487    ///
488    /// 3. For axes with length > 1, the pointer cannot move outside the
489    ///    slice.
490    ///
491    /// 4. If the array will be empty (any axes are zero-length), the
492    ///    difference between the least address and greatest address accessible
493    ///    by moving along all axes must be ≤ `v.len()`.
494    ///
495    ///    If the array will not be empty, the difference between the least
496    ///    address and greatest address accessible by moving along all axes
497    ///    must be < `v.len()`.
498    ///
499    /// 5. The strides must not allow any element to be referenced by two different
500    ///    indices.
501    pub unsafe fn from_shape_vec_unchecked<Sh>(shape: Sh, v: Vec<A>) -> Self
502    where Sh: Into<StrideShape<D>>
503    {
504        let shape = shape.into();
505        let dim = shape.dim;
506        let strides = shape.strides.strides_for_dim(&dim);
507        Self::from_vec_dim_stride_unchecked(dim, strides, v)
508    }
509
510    unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec<A>) -> Self
511    {
512        // debug check for issues that indicates wrong use of this constructor
513        debug_assert!(dimension::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok());
514
515        let ptr = nonnull_from_vec_data(&mut v).add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides));
516        ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim)
517    }
518
519    /// Creates an array from an iterator, mapped by `map` and interpret it according to the
520    /// provided shape and strides.
521    ///
522    /// # Safety
523    ///
524    /// See from_shape_vec_unchecked
525    pub(crate) unsafe fn from_shape_trusted_iter_unchecked<Sh, I, F>(shape: Sh, iter: I, map: F) -> Self
526    where
527        Sh: Into<StrideShape<D>>,
528        I: TrustedIterator + ExactSizeIterator,
529        F: FnMut(I::Item) -> A,
530    {
531        let shape = shape.into();
532        let dim = shape.dim;
533        let strides = shape.strides.strides_for_dim(&dim);
534        let v = to_vec_mapped(iter, map);
535        Self::from_vec_dim_stride_unchecked(dim, strides, v)
536    }
537
538    /// Create an array with uninitialized elements, shape `shape`.
539    ///
540    /// The uninitialized elements of type `A` are represented by the type `MaybeUninit<A>`,
541    /// an easier way to handle uninit values correctly.
542    ///
543    /// Only *when* the array is completely initialized with valid elements, can it be
544    /// converted to an array of `A` elements using [`.assume_init()`].
545    ///
546    /// **Panics** if the number of elements in `shape` would overflow isize.
547    ///
548    /// ### Safety
549    ///
550    /// The whole of the array must be initialized before it is converted
551    /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`.
552    ///
553    /// ### Examples
554    ///
555    /// It is possible to assign individual values through `*elt = MaybeUninit::new(value)`
556    /// and so on.
557    ///
558    /// [`.assume_init()`]: ArrayBase::assume_init
559    ///
560    /// ```
561    /// use ndarray::{s, Array2};
562    ///
563    /// // Example Task: Let's create a column shifted copy of the input
564    ///
565    /// fn shift_by_two(a: &Array2<f32>) -> Array2<f32> {
566    ///     // create an uninitialized array
567    ///     let mut b = Array2::uninit(a.dim());
568    ///
569    ///     // two first columns in b are two last in a
570    ///     // rest of columns in b are the initial columns in a
571    ///
572    ///     a.slice(s![.., -2..]).assign_to(b.slice_mut(s![.., ..2]));
573    ///     a.slice(s![.., 2..]).assign_to(b.slice_mut(s![.., ..-2]));
574    ///
575    ///     // Now we can promise that `b` is safe to use with all operations
576    ///     unsafe {
577    ///         b.assume_init()
578    ///     }
579    /// }
580    ///
581    /// # let _ = shift_by_two;
582    /// ```
583    pub fn uninit<Sh>(shape: Sh) -> ArrayBase<S::MaybeUninit, D>
584    where Sh: ShapeBuilder<Dim = D>
585    {
586        unsafe {
587            let shape = shape.into_shape_with_order();
588            let size = size_of_shape_checked_unwrap!(&shape.dim);
589            let mut v = Vec::with_capacity(size);
590            v.set_len(size);
591            ArrayBase::from_shape_vec_unchecked(shape, v)
592        }
593    }
594
595    /// Create an array with uninitialized elements, shape `shape`.
596    ///
597    /// The uninitialized elements of type `A` are represented by the type `MaybeUninit<A>`,
598    /// an easier way to handle uninit values correctly.
599    ///
600    /// The `builder` closure gets unshared access to the array through a view and can use it to
601    /// modify the array before it is returned. This allows initializing the array for any owned
602    /// array type (avoiding clone requirements for copy-on-write, because the array is unshared
603    /// when initially created).
604    ///
605    /// Only *when* the array is completely initialized with valid elements, can it be
606    /// converted to an array of `A` elements using [`.assume_init()`].
607    ///
608    /// **Panics** if the number of elements in `shape` would overflow isize.
609    ///
610    /// ### Safety
611    ///
612    /// The whole of the array must be initialized before it is converted
613    /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`.
614    ///
615    /// [`.assume_init()`]: ArrayBase::assume_init
616    pub fn build_uninit<Sh, F>(shape: Sh, builder: F) -> ArrayBase<S::MaybeUninit, D>
617    where
618        Sh: ShapeBuilder<Dim = D>,
619        F: FnOnce(ArrayViewMut<MaybeUninit<A>, D>),
620    {
621        let mut array = Self::uninit(shape);
622        // Safe because: the array is unshared here
623        unsafe {
624            builder(array.raw_view_mut_unchecked().deref_into_view_mut());
625        }
626        array
627    }
628}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy