Buckets:
MisterAI/LocalAI_Demo_backends / cpu-diffusers.upgrade-tmp /venv /lib /python3.10 /site-packages /sympy /sets /fancysets.py
| from functools import reduce | |
| from itertools import product | |
| from sympy.core.basic import Basic | |
| from sympy.core.containers import Tuple | |
| from sympy.core.expr import Expr | |
| from sympy.core.function import Lambda | |
| from sympy.core.logic import fuzzy_not, fuzzy_or, fuzzy_and | |
| from sympy.core.mod import Mod | |
| from sympy.core.intfunc import igcd | |
| from sympy.core.numbers import oo, Rational, Integer | |
| from sympy.core.relational import Eq, is_eq | |
| from sympy.core.kind import NumberKind | |
| from sympy.core.singleton import Singleton, S | |
| from sympy.core.symbol import Dummy, symbols, Symbol | |
| from sympy.core.sympify import _sympify, sympify, _sympy_converter | |
| from sympy.functions.elementary.integers import ceiling, floor | |
| from sympy.functions.elementary.trigonometric import sin, cos | |
| from sympy.logic.boolalg import And, Or | |
| from .sets import tfn, Set, Interval, Union, FiniteSet, ProductSet, SetKind | |
| from sympy.utilities.misc import filldedent | |
| class Rationals(Set, metaclass=Singleton): | |
| """ | |
| Represents the rational numbers. This set is also available as | |
| the singleton ``S.Rationals``. | |
| Examples | |
| ======== | |
| >>> from sympy import S | |
| >>> S.Half in S.Rationals | |
| True | |
| >>> iterable = iter(S.Rationals) | |
| >>> [next(iterable) for i in range(12)] | |
| [0, 1, -1, 1/2, 2, -1/2, -2, 1/3, 3, -1/3, -3, 2/3] | |
| """ | |
| is_iterable = True | |
| _inf = S.NegativeInfinity | |
| _sup = S.Infinity | |
| is_empty = False | |
| is_finite_set = False | |
| def _contains(self, other): | |
| if not isinstance(other, Expr): | |
| return S.false | |
| return tfn[other.is_rational] | |
| def __iter__(self): | |
| yield S.Zero | |
| yield S.One | |
| yield S.NegativeOne | |
| d = 2 | |
| while True: | |
| for n in range(d): | |
| if igcd(n, d) == 1: | |
| yield Rational(n, d) | |
| yield Rational(d, n) | |
| yield Rational(-n, d) | |
| yield Rational(-d, n) | |
| d += 1 | |
| def _boundary(self): | |
| return S.Reals | |
| def _kind(self): | |
| return SetKind(NumberKind) | |
| class Naturals(Set, metaclass=Singleton): | |
| """ | |
| Represents the natural numbers (or counting numbers) which are all | |
| positive integers starting from 1. This set is also available as | |
| the singleton ``S.Naturals``. | |
| Examples | |
| ======== | |
| >>> from sympy import S, Interval, pprint | |
| >>> 5 in S.Naturals | |
| True | |
| >>> iterable = iter(S.Naturals) | |
| >>> next(iterable) | |
| 1 | |
| >>> next(iterable) | |
| 2 | |
| >>> next(iterable) | |
| 3 | |
| >>> pprint(S.Naturals.intersect(Interval(0, 10))) | |
| {1, 2, ..., 10} | |
| See Also | |
| ======== | |
| Naturals0 : non-negative integers (i.e. includes 0, too) | |
| Integers : also includes negative integers | |
| """ | |
| is_iterable = True | |
| _inf: Integer = S.One | |
| _sup = S.Infinity | |
| is_empty = False | |
| is_finite_set = False | |
| def _contains(self, other): | |
| if not isinstance(other, Expr): | |
| return S.false | |
| elif other.is_positive and other.is_integer: | |
| return S.true | |
| elif other.is_integer is False or other.is_positive is False: | |
| return S.false | |
| def _eval_is_subset(self, other): | |
| return Range(1, oo).is_subset(other) | |
| def _eval_is_superset(self, other): | |
| return Range(1, oo).is_superset(other) | |
| def __iter__(self): | |
| i = self._inf | |
| while True: | |
| yield i | |
| i = i + 1 | |
| def _boundary(self): | |
| return self | |
| def as_relational(self, x): | |
| return And(Eq(floor(x), x), x >= self.inf, x < oo) | |
| def _kind(self): | |
| return SetKind(NumberKind) | |
| class Naturals0(Naturals): | |
| """Represents the whole numbers which are all the non-negative integers, | |
| inclusive of zero. | |
| See Also | |
| ======== | |
| Naturals : positive integers; does not include 0 | |
| Integers : also includes the negative integers | |
| """ | |
| _inf = S.Zero | |
| def _contains(self, other): | |
| if not isinstance(other, Expr): | |
| return S.false | |
| elif other.is_integer and other.is_nonnegative: | |
| return S.true | |
| elif other.is_integer is False or other.is_nonnegative is False: | |
| return S.false | |
| def _eval_is_subset(self, other): | |
| return Range(oo).is_subset(other) | |
| def _eval_is_superset(self, other): | |
| return Range(oo).is_superset(other) | |
| class Integers(Set, metaclass=Singleton): | |
| """ | |
| Represents all integers: positive, negative and zero. This set is also | |
| available as the singleton ``S.Integers``. | |
| Examples | |
| ======== | |
| >>> from sympy import S, Interval, pprint | |
| >>> 5 in S.Naturals | |
| True | |
| >>> iterable = iter(S.Integers) | |
| >>> next(iterable) | |
| 0 | |
| >>> next(iterable) | |
| 1 | |
| >>> next(iterable) | |
| -1 | |
| >>> next(iterable) | |
| 2 | |
| >>> pprint(S.Integers.intersect(Interval(-4, 4))) | |
| {-4, -3, ..., 4} | |
| See Also | |
| ======== | |
| Naturals0 : non-negative integers | |
| Integers : positive and negative integers and zero | |
| """ | |
| is_iterable = True | |
| is_empty = False | |
| is_finite_set = False | |
| def _contains(self, other): | |
| if not isinstance(other, Expr): | |
| return S.false | |
| return tfn[other.is_integer] | |
| def __iter__(self): | |
| yield S.Zero | |
| i = S.One | |
| while True: | |
| yield i | |
| yield -i | |
| i = i + 1 | |
| def _inf(self): | |
| return S.NegativeInfinity | |
| def _sup(self): | |
| return S.Infinity | |
| def _boundary(self): | |
| return self | |
| def _kind(self): | |
| return SetKind(NumberKind) | |
| def as_relational(self, x): | |
| return And(Eq(floor(x), x), -oo < x, x < oo) | |
| def _eval_is_subset(self, other): | |
| return Range(-oo, oo).is_subset(other) | |
| def _eval_is_superset(self, other): | |
| return Range(-oo, oo).is_superset(other) | |
| class Reals(Interval, metaclass=Singleton): | |
| """ | |
| Represents all real numbers | |
| from negative infinity to positive infinity, | |
| including all integer, rational and irrational numbers. | |
| This set is also available as the singleton ``S.Reals``. | |
| Examples | |
| ======== | |
| >>> from sympy import S, Rational, pi, I | |
| >>> 5 in S.Reals | |
| True | |
| >>> Rational(-1, 2) in S.Reals | |
| True | |
| >>> pi in S.Reals | |
| True | |
| >>> 3*I in S.Reals | |
| False | |
| >>> S.Reals.contains(pi) | |
| True | |
| See Also | |
| ======== | |
| ComplexRegion | |
| """ | |
| def start(self): | |
| return S.NegativeInfinity | |
| def end(self): | |
| return S.Infinity | |
| def left_open(self): | |
| return True | |
| def right_open(self): | |
| return True | |
| def __eq__(self, other): | |
| return other == Interval(S.NegativeInfinity, S.Infinity) | |
| def __hash__(self): | |
| return hash(Interval(S.NegativeInfinity, S.Infinity)) | |
| class ImageSet(Set): | |
| """ | |
| Image of a set under a mathematical function. The transformation | |
| must be given as a Lambda function which has as many arguments | |
| as the elements of the set upon which it operates, e.g. 1 argument | |
| when acting on the set of integers or 2 arguments when acting on | |
| a complex region. | |
| This function is not normally called directly, but is called | |
| from ``imageset``. | |
| Examples | |
| ======== | |
| >>> from sympy import Symbol, S, pi, Dummy, Lambda | |
| >>> from sympy import FiniteSet, ImageSet, Interval | |
| >>> x = Symbol('x') | |
| >>> N = S.Naturals | |
| >>> squares = ImageSet(Lambda(x, x**2), N) # {x**2 for x in N} | |
| >>> 4 in squares | |
| True | |
| >>> 5 in squares | |
| False | |
| >>> FiniteSet(0, 1, 2, 3, 4, 5, 6, 7, 9, 10).intersect(squares) | |
| {1, 4, 9} | |
| >>> square_iterable = iter(squares) | |
| >>> for i in range(4): | |
| ... next(square_iterable) | |
| 1 | |
| 4 | |
| 9 | |
| 16 | |
| If you want to get value for `x` = 2, 1/2 etc. (Please check whether the | |
| `x` value is in ``base_set`` or not before passing it as args) | |
| >>> squares.lamda(2) | |
| 4 | |
| >>> squares.lamda(S(1)/2) | |
| 1/4 | |
| >>> n = Dummy('n') | |
| >>> solutions = ImageSet(Lambda(n, n*pi), S.Integers) # solutions of sin(x) = 0 | |
| >>> dom = Interval(-1, 1) | |
| >>> dom.intersect(solutions) | |
| {0} | |
| See Also | |
| ======== | |
| sympy.sets.sets.imageset | |
| """ | |
| def __new__(cls, flambda, *sets): | |
| if not isinstance(flambda, Lambda): | |
| raise ValueError('First argument must be a Lambda') | |
| signature = flambda.signature | |
| if len(signature) != len(sets): | |
| raise ValueError('Incompatible signature') | |
| sets = [_sympify(s) for s in sets] | |
| if not all(isinstance(s, Set) for s in sets): | |
| raise TypeError("Set arguments to ImageSet should of type Set") | |
| if not all(cls._check_sig(sg, st) for sg, st in zip(signature, sets)): | |
| raise ValueError("Signature %s does not match sets %s" % (signature, sets)) | |
| if flambda is S.IdentityFunction and len(sets) == 1: | |
| return sets[0] | |
| if not set(flambda.variables) & flambda.expr.free_symbols: | |
| is_empty = fuzzy_or(s.is_empty for s in sets) | |
| if is_empty == True: | |
| return S.EmptySet | |
| elif is_empty == False: | |
| return FiniteSet(flambda.expr) | |
| return Basic.__new__(cls, flambda, *sets) | |
| lamda = property(lambda self: self.args[0]) | |
| base_sets = property(lambda self: self.args[1:]) | |
| def base_set(self): | |
| # XXX: Maybe deprecate this? It is poorly defined in handling | |
| # the multivariate case... | |
| sets = self.base_sets | |
| if len(sets) == 1: | |
| return sets[0] | |
| else: | |
| return ProductSet(*sets).flatten() | |
| def base_pset(self): | |
| return ProductSet(*self.base_sets) | |
| def _check_sig(cls, sig_i, set_i): | |
| if sig_i.is_symbol: | |
| return True | |
| elif isinstance(set_i, ProductSet): | |
| sets = set_i.sets | |
| if len(sig_i) != len(sets): | |
| return False | |
| # Recurse through the signature for nested tuples: | |
| return all(cls._check_sig(ts, ps) for ts, ps in zip(sig_i, sets)) | |
| else: | |
| # XXX: Need a better way of checking whether a set is a set of | |
| # Tuples or not. For example a FiniteSet can contain Tuples | |
| # but so can an ImageSet or a ConditionSet. Others like | |
| # Integers, Reals etc can not contain Tuples. We could just | |
| # list the possibilities here... Current code for e.g. | |
| # _contains probably only works for ProductSet. | |
| return True # Give the benefit of the doubt | |
| def __iter__(self): | |
| already_seen = set() | |
| for i in self.base_pset: | |
| val = self.lamda(*i) | |
| if val in already_seen: | |
| continue | |
| else: | |
| already_seen.add(val) | |
| yield val | |
| def _is_multivariate(self): | |
| return len(self.lamda.variables) > 1 | |
| def _contains(self, other): | |
| from sympy.solvers.solveset import _solveset_multi | |
| def get_symsetmap(signature, base_sets): | |
| '''Attempt to get a map of symbols to base_sets''' | |
| queue = list(zip(signature, base_sets)) | |
| symsetmap = {} | |
| for sig, base_set in queue: | |
| if sig.is_symbol: | |
| symsetmap[sig] = base_set | |
| elif base_set.is_ProductSet: | |
| sets = base_set.sets | |
| if len(sig) != len(sets): | |
| raise ValueError("Incompatible signature") | |
| # Recurse | |
| queue.extend(zip(sig, sets)) | |
| else: | |
| # If we get here then we have something like sig = (x, y) and | |
| # base_set = {(1, 2), (3, 4)}. For now we give up. | |
| return None | |
| return symsetmap | |
| def get_equations(expr, candidate): | |
| '''Find the equations relating symbols in expr and candidate.''' | |
| queue = [(expr, candidate)] | |
| for e, c in queue: | |
| if not isinstance(e, Tuple): | |
| yield Eq(e, c) | |
| elif not isinstance(c, Tuple) or len(e) != len(c): | |
| yield False | |
| return | |
| else: | |
| queue.extend(zip(e, c)) | |
| # Get the basic objects together: | |
| other = _sympify(other) | |
| expr = self.lamda.expr | |
| sig = self.lamda.signature | |
| variables = self.lamda.variables | |
| base_sets = self.base_sets | |
| # Use dummy symbols for ImageSet parameters so they don't match | |
| # anything in other | |
| rep = {v: Dummy(v.name) for v in variables} | |
| variables = [v.subs(rep) for v in variables] | |
| sig = sig.subs(rep) | |
| expr = expr.subs(rep) | |
| # Map the parts of other to those in the Lambda expr | |
| equations = [] | |
| for eq in get_equations(expr, other): | |
| # Unsatisfiable equation? | |
| if eq is False: | |
| return S.false | |
| equations.append(eq) | |
| # Map the symbols in the signature to the corresponding domains | |
| symsetmap = get_symsetmap(sig, base_sets) | |
| if symsetmap is None: | |
| # Can't factor the base sets to a ProductSet | |
| return None | |
| # Which of the variables in the Lambda signature need to be solved for? | |
| symss = (eq.free_symbols for eq in equations) | |
| variables = set(variables) & reduce(set.union, symss, set()) | |
| # Use internal multivariate solveset | |
| variables = tuple(variables) | |
| base_sets = [symsetmap[v] for v in variables] | |
| solnset = _solveset_multi(equations, variables, base_sets) | |
| if solnset is None: | |
| return None | |
| return tfn[fuzzy_not(solnset.is_empty)] | |
| def is_iterable(self): | |
| return all(s.is_iterable for s in self.base_sets) | |
| def doit(self, **hints): | |
| from sympy.sets.setexpr import SetExpr | |
| f = self.lamda | |
| sig = f.signature | |
| if len(sig) == 1 and sig[0].is_symbol and isinstance(f.expr, Expr): | |
| base_set = self.base_sets[0] | |
| return SetExpr(base_set)._eval_func(f).set | |
| if all(s.is_FiniteSet for s in self.base_sets): | |
| return FiniteSet(*(f(*a) for a in product(*self.base_sets))) | |
| return self | |
| def _kind(self): | |
| return SetKind(self.lamda.expr.kind) | |
| class Range(Set): | |
| """ | |
| Represents a range of integers. Can be called as ``Range(stop)``, | |
| ``Range(start, stop)``, or ``Range(start, stop, step)``; when ``step`` is | |
| not given it defaults to 1. | |
| ``Range(stop)`` is the same as ``Range(0, stop, 1)`` and the stop value | |
| (just as for Python ranges) is not included in the Range values. | |
| >>> from sympy import Range | |
| >>> list(Range(3)) | |
| [0, 1, 2] | |
| The step can also be negative: | |
| >>> list(Range(10, 0, -2)) | |
| [10, 8, 6, 4, 2] | |
| The stop value is made canonical so equivalent ranges always | |
| have the same args: | |
| >>> Range(0, 10, 3) | |
| Range(0, 12, 3) | |
| Infinite ranges are allowed. ``oo`` and ``-oo`` are never included in the | |
| set (``Range`` is always a subset of ``Integers``). If the starting point | |
| is infinite, then the final value is ``stop - step``. To iterate such a | |
| range, it needs to be reversed: | |
| >>> from sympy import oo | |
| >>> r = Range(-oo, 1) | |
| >>> r[-1] | |
| 0 | |
| >>> next(iter(r)) | |
| Traceback (most recent call last): | |
| ... | |
| TypeError: Cannot iterate over Range with infinite start | |
| >>> next(iter(r.reversed)) | |
| 0 | |
| Although ``Range`` is a :class:`Set` (and supports the normal set | |
| operations) it maintains the order of the elements and can | |
| be used in contexts where ``range`` would be used. | |
| >>> from sympy import Interval | |
| >>> Range(0, 10, 2).intersect(Interval(3, 7)) | |
| Range(4, 8, 2) | |
| >>> list(_) | |
| [4, 6] | |
| Although slicing of a Range will always return a Range -- possibly | |
| empty -- an empty set will be returned from any intersection that | |
| is empty: | |
| >>> Range(3)[:0] | |
| Range(0, 0, 1) | |
| >>> Range(3).intersect(Interval(4, oo)) | |
| EmptySet | |
| >>> Range(3).intersect(Range(4, oo)) | |
| EmptySet | |
| Range will accept symbolic arguments but has very limited support | |
| for doing anything other than displaying the Range: | |
| >>> from sympy import Symbol, pprint | |
| >>> from sympy.abc import i, j, k | |
| >>> Range(i, j, k).start | |
| i | |
| >>> Range(i, j, k).inf | |
| Traceback (most recent call last): | |
| ... | |
| ValueError: invalid method for symbolic range | |
| Better success will be had when using integer symbols: | |
| >>> n = Symbol('n', integer=True) | |
| >>> r = Range(n, n + 20, 3) | |
| >>> r.inf | |
| n | |
| >>> pprint(r) | |
| {n, n + 3, ..., n + 18} | |
| """ | |
| def __new__(cls, *args): | |
| if len(args) == 1: | |
| if isinstance(args[0], range): | |
| raise TypeError( | |
| 'use sympify(%s) to convert range to Range' % args[0]) | |
| # expand range | |
| slc = slice(*args) | |
| if slc.step == 0: | |
| raise ValueError("step cannot be 0") | |
| start, stop, step = slc.start or 0, slc.stop, slc.step or 1 | |
| try: | |
| ok = [] | |
| for w in (start, stop, step): | |
| w = sympify(w) | |
| if w in [S.NegativeInfinity, S.Infinity] or ( | |
| w.has(Symbol) and w.is_integer != False): | |
| ok.append(w) | |
| elif not w.is_Integer: | |
| if w.is_infinite: | |
| raise ValueError('infinite symbols not allowed') | |
| raise ValueError | |
| else: | |
| ok.append(w) | |
| except ValueError: | |
| raise ValueError(filldedent(''' | |
| Finite arguments to Range must be integers; `imageset` can define | |
| other cases, e.g. use `imageset(i, i/10, Range(3))` to give | |
| [0, 1/10, 1/5].''')) | |
| start, stop, step = ok | |
| null = False | |
| if any(i.has(Symbol) for i in (start, stop, step)): | |
| dif = stop - start | |
| n = dif/step | |
| if n.is_Rational: | |
| if dif == 0: | |
| null = True | |
| else: # (x, x + 5, 2) or (x, 3*x, x) | |
| n = floor(n) | |
| end = start + n*step | |
| if dif.is_Rational: # (x, x + 5, 2) | |
| if (end - stop).is_negative: | |
| end += step | |
| else: # (x, 3*x, x) | |
| if (end/stop - 1).is_negative: | |
| end += step | |
| elif n.is_extended_negative: | |
| null = True | |
| else: | |
| end = stop # other methods like sup and reversed must fail | |
| elif start.is_infinite: | |
| span = step*(stop - start) | |
| if span is S.NaN or span <= 0: | |
| null = True | |
| elif step.is_Integer and stop.is_infinite and abs(step) != 1: | |
| raise ValueError(filldedent(''' | |
| Step size must be %s in this case.''' % (1 if step > 0 else -1))) | |
| else: | |
| end = stop | |
| else: | |
| oostep = step.is_infinite | |
| if oostep: | |
| step = S.One if step > 0 else S.NegativeOne | |
| n = ceiling((stop - start)/step) | |
| if n <= 0: | |
| null = True | |
| elif oostep: | |
| step = S.One # make it canonical | |
| end = start + step | |
| else: | |
| end = start + n*step | |
| if null: | |
| start = end = S.Zero | |
| step = S.One | |
| return Basic.__new__(cls, start, end, step) | |
| start = property(lambda self: self.args[0]) | |
| stop = property(lambda self: self.args[1]) | |
| step = property(lambda self: self.args[2]) | |
| def reversed(self): | |
| """Return an equivalent Range in the opposite order. | |
| Examples | |
| ======== | |
| >>> from sympy import Range | |
| >>> Range(10).reversed | |
| Range(9, -1, -1) | |
| """ | |
| if self.has(Symbol): | |
| n = (self.stop - self.start)/self.step | |
| if not n.is_extended_positive or not all( | |
| i.is_integer or i.is_infinite for i in self.args): | |
| raise ValueError('invalid method for symbolic range') | |
| if self.start == self.stop: | |
| return self | |
| return self.func( | |
| self.stop - self.step, self.start - self.step, -self.step) | |
| def _kind(self): | |
| return SetKind(NumberKind) | |
| def _contains(self, other): | |
| if self.start == self.stop: | |
| return S.false | |
| if other.is_infinite: | |
| return S.false | |
| if not other.is_integer: | |
| return tfn[other.is_integer] | |
| if self.has(Symbol): | |
| n = (self.stop - self.start)/self.step | |
| if not n.is_extended_positive or not all( | |
| i.is_integer or i.is_infinite for i in self.args): | |
| return | |
| else: | |
| n = self.size | |
| if self.start.is_finite: | |
| ref = self.start | |
| elif self.stop.is_finite: | |
| ref = self.stop | |
| else: # both infinite; step is +/- 1 (enforced by __new__) | |
| return S.true | |
| if n == 1: | |
| return Eq(other, self[0]) | |
| res = (ref - other) % self.step | |
| if res == S.Zero: | |
| if self.has(Symbol): | |
| d = Dummy('i') | |
| return self.as_relational(d).subs(d, other) | |
| return And(other >= self.inf, other <= self.sup) | |
| elif res.is_Integer: # off sequence | |
| return S.false | |
| else: # symbolic/unsimplified residue modulo step | |
| return None | |
| def __iter__(self): | |
| n = self.size # validate | |
| if not (n.has(S.Infinity) or n.has(S.NegativeInfinity) or n.is_Integer): | |
| raise TypeError("Cannot iterate over symbolic Range") | |
| if self.start in [S.NegativeInfinity, S.Infinity]: | |
| raise TypeError("Cannot iterate over Range with infinite start") | |
| elif self.start != self.stop: | |
| i = self.start | |
| if n.is_infinite: | |
| while True: | |
| yield i | |
| i += self.step | |
| else: | |
| for _ in range(n): | |
| yield i | |
| i += self.step | |
| def is_iterable(self): | |
| # Check that size can be determined, used by __iter__ | |
| dif = self.stop - self.start | |
| n = dif/self.step | |
| if not (n.has(S.Infinity) or n.has(S.NegativeInfinity) or n.is_Integer): | |
| return False | |
| if self.start in [S.NegativeInfinity, S.Infinity]: | |
| return False | |
| if not (n.is_extended_nonnegative and all(i.is_integer for i in self.args)): | |
| return False | |
| return True | |
| def __len__(self): | |
| rv = self.size | |
| if rv is S.Infinity: | |
| raise ValueError('Use .size to get the length of an infinite Range') | |
| return int(rv) | |
| def size(self): | |
| if self.start == self.stop: | |
| return S.Zero | |
| dif = self.stop - self.start | |
| n = dif/self.step | |
| if n.is_infinite: | |
| return S.Infinity | |
| if n.is_extended_nonnegative and all(i.is_integer for i in self.args): | |
| return abs(floor(n)) | |
| raise ValueError('Invalid method for symbolic Range') | |
| def is_finite_set(self): | |
| if self.start.is_integer and self.stop.is_integer: | |
| return True | |
| return self.size.is_finite | |
| def is_empty(self): | |
| try: | |
| return self.size.is_zero | |
| except ValueError: | |
| return None | |
| def __bool__(self): | |
| # this only distinguishes between definite null range | |
| # and non-null/unknown null; getting True doesn't mean | |
| # that it actually is not null | |
| b = is_eq(self.start, self.stop) | |
| if b is None: | |
| raise ValueError('cannot tell if Range is null or not') | |
| return not bool(b) | |
| def __getitem__(self, i): | |
| ooslice = "cannot slice from the end with an infinite value" | |
| zerostep = "slice step cannot be zero" | |
| infinite = "slicing not possible on range with infinite start" | |
| # if we had to take every other element in the following | |
| # oo, ..., 6, 4, 2, 0 | |
| # we might get oo, ..., 4, 0 or oo, ..., 6, 2 | |
| ambiguous = "cannot unambiguously re-stride from the end " + \ | |
| "with an infinite value" | |
| if isinstance(i, slice): | |
| if self.size.is_finite: # validates, too | |
| if self.start == self.stop: | |
| return Range(0) | |
| start, stop, step = i.indices(self.size) | |
| n = ceiling((stop - start)/step) | |
| if n <= 0: | |
| return Range(0) | |
| canonical_stop = start + n*step | |
| end = canonical_stop - step | |
| ss = step*self.step | |
| return Range(self[start], self[end] + ss, ss) | |
| else: # infinite Range | |
| start = i.start | |
| stop = i.stop | |
| if i.step == 0: | |
| raise ValueError(zerostep) | |
| step = i.step or 1 | |
| ss = step*self.step | |
| #--------------------- | |
| # handle infinite Range | |
| # i.e. Range(-oo, oo) or Range(oo, -oo, -1) | |
| # -------------------- | |
| if self.start.is_infinite and self.stop.is_infinite: | |
| raise ValueError(infinite) | |
| #--------------------- | |
| # handle infinite on right | |
| # e.g. Range(0, oo) or Range(0, -oo, -1) | |
| # -------------------- | |
| if self.stop.is_infinite: | |
| # start and stop are not interdependent -- | |
| # they only depend on step --so we use the | |
| # equivalent reversed values | |
| return self.reversed[ | |
| stop if stop is None else -stop + 1: | |
| start if start is None else -start: | |
| step].reversed | |
| #--------------------- | |
| # handle infinite on the left | |
| # e.g. Range(oo, 0, -1) or Range(-oo, 0) | |
| # -------------------- | |
| # consider combinations of | |
| # start/stop {== None, < 0, == 0, > 0} and | |
| # step {< 0, > 0} | |
| if start is None: | |
| if stop is None: | |
| if step < 0: | |
| return Range(self[-1], self.start, ss) | |
| elif step > 1: | |
| raise ValueError(ambiguous) | |
| else: # == 1 | |
| return self | |
| elif stop < 0: | |
| if step < 0: | |
| return Range(self[-1], self[stop], ss) | |
| else: # > 0 | |
| return Range(self.start, self[stop], ss) | |
| elif stop == 0: | |
| if step > 0: | |
| return Range(0) | |
| else: # < 0 | |
| raise ValueError(ooslice) | |
| elif stop == 1: | |
| if step > 0: | |
| raise ValueError(ooslice) # infinite singleton | |
| else: # < 0 | |
| raise ValueError(ooslice) | |
| else: # > 1 | |
| raise ValueError(ooslice) | |
| elif start < 0: | |
| if stop is None: | |
| if step < 0: | |
| return Range(self[start], self.start, ss) | |
| else: # > 0 | |
| return Range(self[start], self.stop, ss) | |
| elif stop < 0: | |
| return Range(self[start], self[stop], ss) | |
| elif stop == 0: | |
| if step < 0: | |
| raise ValueError(ooslice) | |
| else: # > 0 | |
| return Range(0) | |
| elif stop > 0: | |
| raise ValueError(ooslice) | |
| elif start == 0: | |
| if stop is None: | |
| if step < 0: | |
| raise ValueError(ooslice) # infinite singleton | |
| elif step > 1: | |
| raise ValueError(ambiguous) | |
| else: # == 1 | |
| return self | |
| elif stop < 0: | |
| if step > 1: | |
| raise ValueError(ambiguous) | |
| elif step == 1: | |
| return Range(self.start, self[stop], ss) | |
| else: # < 0 | |
| return Range(0) | |
| else: # >= 0 | |
| raise ValueError(ooslice) | |
| elif start > 0: | |
| raise ValueError(ooslice) | |
| else: | |
| if self.start == self.stop: | |
| raise IndexError('Range index out of range') | |
| if not (all(i.is_integer or i.is_infinite | |
| for i in self.args) and ((self.stop - self.start)/ | |
| self.step).is_extended_positive): | |
| raise ValueError('Invalid method for symbolic Range') | |
| if i == 0: | |
| if self.start.is_infinite: | |
| raise ValueError(ooslice) | |
| return self.start | |
| if i == -1: | |
| if self.stop.is_infinite: | |
| raise ValueError(ooslice) | |
| return self.stop - self.step | |
| n = self.size # must be known for any other index | |
| rv = (self.stop if i < 0 else self.start) + i*self.step | |
| if rv.is_infinite: | |
| raise ValueError(ooslice) | |
| val = (rv - self.start)/self.step | |
| rel = fuzzy_or([val.is_infinite, | |
| fuzzy_and([val.is_nonnegative, (n-val).is_nonnegative])]) | |
| if rel: | |
| return rv | |
| if rel is None: | |
| raise ValueError('Invalid method for symbolic Range') | |
| raise IndexError("Range index out of range") | |
| def _inf(self): | |
| if not self: | |
| return S.EmptySet.inf | |
| if self.has(Symbol): | |
| if all(i.is_integer or i.is_infinite for i in self.args): | |
| dif = self.stop - self.start | |
| if self.step.is_positive and dif.is_positive: | |
| return self.start | |
| elif self.step.is_negative and dif.is_negative: | |
| return self.stop - self.step | |
| raise ValueError('invalid method for symbolic range') | |
| if self.step > 0: | |
| return self.start | |
| else: | |
| return self.stop - self.step | |
| def _sup(self): | |
| if not self: | |
| return S.EmptySet.sup | |
| if self.has(Symbol): | |
| if all(i.is_integer or i.is_infinite for i in self.args): | |
| dif = self.stop - self.start | |
| if self.step.is_positive and dif.is_positive: | |
| return self.stop - self.step | |
| elif self.step.is_negative and dif.is_negative: | |
| return self.start | |
| raise ValueError('invalid method for symbolic range') | |
| if self.step > 0: | |
| return self.stop - self.step | |
| else: | |
| return self.start | |
| def _boundary(self): | |
| return self | |
| def as_relational(self, x): | |
| """Rewrite a Range in terms of equalities and logic operators. """ | |
| if self.start.is_infinite: | |
| assert not self.stop.is_infinite # by instantiation | |
| a = self.reversed.start | |
| else: | |
| a = self.start | |
| step = self.step | |
| in_seq = Eq(Mod(x - a, step), 0) | |
| ints = And(Eq(Mod(a, 1), 0), Eq(Mod(step, 1), 0)) | |
| n = (self.stop - self.start)/self.step | |
| if n == 0: | |
| return S.EmptySet.as_relational(x) | |
| if n == 1: | |
| return And(Eq(x, a), ints) | |
| try: | |
| a, b = self.inf, self.sup | |
| except ValueError: | |
| a = None | |
| if a is not None: | |
| range_cond = And( | |
| x > a if a.is_infinite else x >= a, | |
| x < b if b.is_infinite else x <= b) | |
| else: | |
| a, b = self.start, self.stop - self.step | |
| range_cond = Or( | |
| And(self.step >= 1, x > a if a.is_infinite else x >= a, | |
| x < b if b.is_infinite else x <= b), | |
| And(self.step <= -1, x < a if a.is_infinite else x <= a, | |
| x > b if b.is_infinite else x >= b)) | |
| return And(in_seq, ints, range_cond) | |
| _sympy_converter[range] = lambda r: Range(r.start, r.stop, r.step) | |
| def normalize_theta_set(theta): | |
| r""" | |
| Normalize a Real Set `theta` in the interval `[0, 2\pi)`. It returns | |
| a normalized value of theta in the Set. For Interval, a maximum of | |
| one cycle $[0, 2\pi]$, is returned i.e. for theta equal to $[0, 10\pi]$, | |
| returned normalized value would be $[0, 2\pi)$. As of now intervals | |
| with end points as non-multiples of ``pi`` is not supported. | |
| Raises | |
| ====== | |
| NotImplementedError | |
| The algorithms for Normalizing theta Set are not yet | |
| implemented. | |
| ValueError | |
| The input is not valid, i.e. the input is not a real set. | |
| RuntimeError | |
| It is a bug, please report to the github issue tracker. | |
| Examples | |
| ======== | |
| >>> from sympy.sets.fancysets import normalize_theta_set | |
| >>> from sympy import Interval, FiniteSet, pi | |
| >>> normalize_theta_set(Interval(9*pi/2, 5*pi)) | |
| Interval(pi/2, pi) | |
| >>> normalize_theta_set(Interval(-3*pi/2, pi/2)) | |
| Interval.Ropen(0, 2*pi) | |
| >>> normalize_theta_set(Interval(-pi/2, pi/2)) | |
| Union(Interval(0, pi/2), Interval.Ropen(3*pi/2, 2*pi)) | |
| >>> normalize_theta_set(Interval(-4*pi, 3*pi)) | |
| Interval.Ropen(0, 2*pi) | |
| >>> normalize_theta_set(Interval(-3*pi/2, -pi/2)) | |
| Interval(pi/2, 3*pi/2) | |
| >>> normalize_theta_set(FiniteSet(0, pi, 3*pi)) | |
| {0, pi} | |
| """ | |
| from sympy.functions.elementary.trigonometric import _pi_coeff | |
| if theta.is_Interval: | |
| interval_len = theta.measure | |
| # one complete circle | |
| if interval_len >= 2*S.Pi: | |
| if interval_len == 2*S.Pi and theta.left_open and theta.right_open: | |
| k = _pi_coeff(theta.start) | |
| return Union(Interval(0, k*S.Pi, False, True), | |
| Interval(k*S.Pi, 2*S.Pi, True, True)) | |
| return Interval(0, 2*S.Pi, False, True) | |
| k_start, k_end = _pi_coeff(theta.start), _pi_coeff(theta.end) | |
| if k_start is None or k_end is None: | |
| raise NotImplementedError("Normalizing theta without pi as coefficient is " | |
| "not yet implemented") | |
| new_start = k_start*S.Pi | |
| new_end = k_end*S.Pi | |
| if new_start > new_end: | |
| return Union(Interval(S.Zero, new_end, False, theta.right_open), | |
| Interval(new_start, 2*S.Pi, theta.left_open, True)) | |
| else: | |
| return Interval(new_start, new_end, theta.left_open, theta.right_open) | |
| elif theta.is_FiniteSet: | |
| new_theta = [] | |
| for element in theta: | |
| k = _pi_coeff(element) | |
| if k is None: | |
| raise NotImplementedError('Normalizing theta without pi as ' | |
| 'coefficient, is not Implemented.') | |
| else: | |
| new_theta.append(k*S.Pi) | |
| return FiniteSet(*new_theta) | |
| elif theta.is_Union: | |
| return Union(*[normalize_theta_set(interval) for interval in theta.args]) | |
| elif theta.is_subset(S.Reals): | |
| raise NotImplementedError("Normalizing theta when, it is of type %s is not " | |
| "implemented" % type(theta)) | |
| else: | |
| raise ValueError(" %s is not a real set" % (theta)) | |
| class ComplexRegion(Set): | |
| r""" | |
| Represents the Set of all Complex Numbers. It can represent a | |
| region of Complex Plane in both the standard forms Polar and | |
| Rectangular coordinates. | |
| * Polar Form | |
| Input is in the form of the ProductSet or Union of ProductSets | |
| of the intervals of ``r`` and ``theta``, and use the flag ``polar=True``. | |
| .. math:: Z = \{z \in \mathbb{C} \mid z = r\times (\cos(\theta) + I\sin(\theta)), r \in [\texttt{r}], \theta \in [\texttt{theta}]\} | |
| * Rectangular Form | |
| Input is in the form of the ProductSet or Union of ProductSets | |
| of interval of x and y, the real and imaginary parts of the Complex numbers in a plane. | |
| Default input type is in rectangular form. | |
| .. math:: Z = \{z \in \mathbb{C} \mid z = x + Iy, x \in [\operatorname{re}(z)], y \in [\operatorname{im}(z)]\} | |
| Examples | |
| ======== | |
| >>> from sympy import ComplexRegion, Interval, S, I, Union | |
| >>> a = Interval(2, 3) | |
| >>> b = Interval(4, 6) | |
| >>> c1 = ComplexRegion(a*b) # Rectangular Form | |
| >>> c1 | |
| CartesianComplexRegion(ProductSet(Interval(2, 3), Interval(4, 6))) | |
| * c1 represents the rectangular region in complex plane | |
| surrounded by the coordinates (2, 4), (3, 4), (3, 6) and | |
| (2, 6), of the four vertices. | |
| >>> c = Interval(1, 8) | |
| >>> c2 = ComplexRegion(Union(a*b, b*c)) | |
| >>> c2 | |
| CartesianComplexRegion(Union(ProductSet(Interval(2, 3), Interval(4, 6)), ProductSet(Interval(4, 6), Interval(1, 8)))) | |
| * c2 represents the Union of two rectangular regions in complex | |
| plane. One of them surrounded by the coordinates of c1 and | |
| other surrounded by the coordinates (4, 1), (6, 1), (6, 8) and | |
| (4, 8). | |
| >>> 2.5 + 4.5*I in c1 | |
| True | |
| >>> 2.5 + 6.5*I in c1 | |
| False | |
| >>> r = Interval(0, 1) | |
| >>> theta = Interval(0, 2*S.Pi) | |
| >>> c2 = ComplexRegion(r*theta, polar=True) # Polar Form | |
| >>> c2 # unit Disk | |
| PolarComplexRegion(ProductSet(Interval(0, 1), Interval.Ropen(0, 2*pi))) | |
| * c2 represents the region in complex plane inside the | |
| Unit Disk centered at the origin. | |
| >>> 0.5 + 0.5*I in c2 | |
| True | |
| >>> 1 + 2*I in c2 | |
| False | |
| >>> unit_disk = ComplexRegion(Interval(0, 1)*Interval(0, 2*S.Pi), polar=True) | |
| >>> upper_half_unit_disk = ComplexRegion(Interval(0, 1)*Interval(0, S.Pi), polar=True) | |
| >>> intersection = unit_disk.intersect(upper_half_unit_disk) | |
| >>> intersection | |
| PolarComplexRegion(ProductSet(Interval(0, 1), Interval(0, pi))) | |
| >>> intersection == upper_half_unit_disk | |
| True | |
| See Also | |
| ======== | |
| CartesianComplexRegion | |
| PolarComplexRegion | |
| Complexes | |
| """ | |
| is_ComplexRegion = True | |
| def __new__(cls, sets, polar=False): | |
| if polar is False: | |
| return CartesianComplexRegion(sets) | |
| elif polar is True: | |
| return PolarComplexRegion(sets) | |
| else: | |
| raise ValueError("polar should be either True or False") | |
| def sets(self): | |
| """ | |
| Return raw input sets to the self. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion, Union | |
| >>> a = Interval(2, 3) | |
| >>> b = Interval(4, 5) | |
| >>> c = Interval(1, 7) | |
| >>> C1 = ComplexRegion(a*b) | |
| >>> C1.sets | |
| ProductSet(Interval(2, 3), Interval(4, 5)) | |
| >>> C2 = ComplexRegion(Union(a*b, b*c)) | |
| >>> C2.sets | |
| Union(ProductSet(Interval(2, 3), Interval(4, 5)), ProductSet(Interval(4, 5), Interval(1, 7))) | |
| """ | |
| return self.args[0] | |
| def psets(self): | |
| """ | |
| Return a tuple of sets (ProductSets) input of the self. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion, Union | |
| >>> a = Interval(2, 3) | |
| >>> b = Interval(4, 5) | |
| >>> c = Interval(1, 7) | |
| >>> C1 = ComplexRegion(a*b) | |
| >>> C1.psets | |
| (ProductSet(Interval(2, 3), Interval(4, 5)),) | |
| >>> C2 = ComplexRegion(Union(a*b, b*c)) | |
| >>> C2.psets | |
| (ProductSet(Interval(2, 3), Interval(4, 5)), ProductSet(Interval(4, 5), Interval(1, 7))) | |
| """ | |
| if self.sets.is_ProductSet: | |
| psets = () | |
| psets = psets + (self.sets, ) | |
| else: | |
| psets = self.sets.args | |
| return psets | |
| def a_interval(self): | |
| """ | |
| Return the union of intervals of `x` when, self is in | |
| rectangular form, or the union of intervals of `r` when | |
| self is in polar form. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion, Union | |
| >>> a = Interval(2, 3) | |
| >>> b = Interval(4, 5) | |
| >>> c = Interval(1, 7) | |
| >>> C1 = ComplexRegion(a*b) | |
| >>> C1.a_interval | |
| Interval(2, 3) | |
| >>> C2 = ComplexRegion(Union(a*b, b*c)) | |
| >>> C2.a_interval | |
| Union(Interval(2, 3), Interval(4, 5)) | |
| """ | |
| a_interval = [] | |
| for element in self.psets: | |
| a_interval.append(element.args[0]) | |
| a_interval = Union(*a_interval) | |
| return a_interval | |
| def b_interval(self): | |
| """ | |
| Return the union of intervals of `y` when, self is in | |
| rectangular form, or the union of intervals of `theta` | |
| when self is in polar form. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion, Union | |
| >>> a = Interval(2, 3) | |
| >>> b = Interval(4, 5) | |
| >>> c = Interval(1, 7) | |
| >>> C1 = ComplexRegion(a*b) | |
| >>> C1.b_interval | |
| Interval(4, 5) | |
| >>> C2 = ComplexRegion(Union(a*b, b*c)) | |
| >>> C2.b_interval | |
| Interval(1, 7) | |
| """ | |
| b_interval = [] | |
| for element in self.psets: | |
| b_interval.append(element.args[1]) | |
| b_interval = Union(*b_interval) | |
| return b_interval | |
| def _measure(self): | |
| """ | |
| The measure of self.sets. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion, S | |
| >>> a, b = Interval(2, 5), Interval(4, 8) | |
| >>> c = Interval(0, 2*S.Pi) | |
| >>> c1 = ComplexRegion(a*b) | |
| >>> c1.measure | |
| 12 | |
| >>> c2 = ComplexRegion(a*c, polar=True) | |
| >>> c2.measure | |
| 6*pi | |
| """ | |
| return self.sets._measure | |
| def _kind(self): | |
| return self.args[0].kind | |
| def from_real(cls, sets): | |
| """ | |
| Converts given subset of real numbers to a complex region. | |
| Examples | |
| ======== | |
| >>> from sympy import Interval, ComplexRegion | |
| >>> unit = Interval(0,1) | |
| >>> ComplexRegion.from_real(unit) | |
| CartesianComplexRegion(ProductSet(Interval(0, 1), {0})) | |
| """ | |
| if not sets.is_subset(S.Reals): | |
| raise ValueError("sets must be a subset of the real line") | |
| return CartesianComplexRegion(sets * FiniteSet(0)) | |
| def _contains(self, other): | |
| from sympy.functions import arg, Abs | |
| isTuple = isinstance(other, Tuple) | |
| if isTuple and len(other) != 2: | |
| raise ValueError('expecting Tuple of length 2') | |
| # If the other is not an Expression, and neither a Tuple | |
| if not isinstance(other, (Expr, Tuple)): | |
| return S.false | |
| # self in rectangular form | |
| if not self.polar: | |
| re, im = other if isTuple else other.as_real_imag() | |
| return tfn[fuzzy_or(fuzzy_and([ | |
| pset.args[0]._contains(re), | |
| pset.args[1]._contains(im)]) | |
| for pset in self.psets)] | |
| # self in polar form | |
| elif self.polar: | |
| if other.is_zero: | |
| # ignore undefined complex argument | |
| return tfn[fuzzy_or(pset.args[0]._contains(S.Zero) | |
| for pset in self.psets)] | |
| if isTuple: | |
| r, theta = other | |
| else: | |
| r, theta = Abs(other), arg(other) | |
| if theta.is_real and theta.is_number: | |
| # angles in psets are normalized to [0, 2pi) | |
| theta %= 2*S.Pi | |
| return tfn[fuzzy_or(fuzzy_and([ | |
| pset.args[0]._contains(r), | |
| pset.args[1]._contains(theta)]) | |
| for pset in self.psets)] | |
| class CartesianComplexRegion(ComplexRegion): | |
| r""" | |
| Set representing a square region of the complex plane. | |
| .. math:: Z = \{z \in \mathbb{C} \mid z = x + Iy, x \in [\operatorname{re}(z)], y \in [\operatorname{im}(z)]\} | |
| Examples | |
| ======== | |
| >>> from sympy import ComplexRegion, I, Interval | |
| >>> region = ComplexRegion(Interval(1, 3) * Interval(4, 6)) | |
| >>> 2 + 5*I in region | |
| True | |
| >>> 5*I in region | |
| False | |
| See also | |
| ======== | |
| ComplexRegion | |
| PolarComplexRegion | |
| Complexes | |
| """ | |
| polar = False | |
| variables = symbols('x, y', cls=Dummy) | |
| def __new__(cls, sets): | |
| if sets == S.Reals*S.Reals: | |
| return S.Complexes | |
| if all(_a.is_FiniteSet for _a in sets.args) and (len(sets.args) == 2): | |
| # ** ProductSet of FiniteSets in the Complex Plane. ** | |
| # For Cases like ComplexRegion({2, 4}*{3}), It | |
| # would return {2 + 3*I, 4 + 3*I} | |
| # FIXME: This should probably be handled with something like: | |
| # return ImageSet(Lambda((x, y), x+I*y), sets).rewrite(FiniteSet) | |
| complex_num = [] | |
| for x in sets.args[0]: | |
| for y in sets.args[1]: | |
| complex_num.append(x + S.ImaginaryUnit*y) | |
| return FiniteSet(*complex_num) | |
| else: | |
| return Set.__new__(cls, sets) | |
| def expr(self): | |
| x, y = self.variables | |
| return x + S.ImaginaryUnit*y | |
| class PolarComplexRegion(ComplexRegion): | |
| r""" | |
| Set representing a polar region of the complex plane. | |
| .. math:: Z = \{z \in \mathbb{C} \mid z = r\times (\cos(\theta) + I\sin(\theta)), r \in [\texttt{r}], \theta \in [\texttt{theta}]\} | |
| Examples | |
| ======== | |
| >>> from sympy import ComplexRegion, Interval, oo, pi, I | |
| >>> rset = Interval(0, oo) | |
| >>> thetaset = Interval(0, pi) | |
| >>> upper_half_plane = ComplexRegion(rset * thetaset, polar=True) | |
| >>> 1 + I in upper_half_plane | |
| True | |
| >>> 1 - I in upper_half_plane | |
| False | |
| See also | |
| ======== | |
| ComplexRegion | |
| CartesianComplexRegion | |
| Complexes | |
| """ | |
| polar = True | |
| variables = symbols('r, theta', cls=Dummy) | |
| def __new__(cls, sets): | |
| new_sets = [] | |
| # sets is Union of ProductSets | |
| if not sets.is_ProductSet: | |
| for k in sets.args: | |
| new_sets.append(k) | |
| # sets is ProductSets | |
| else: | |
| new_sets.append(sets) | |
| # Normalize input theta | |
| for k, v in enumerate(new_sets): | |
| new_sets[k] = ProductSet(v.args[0], | |
| normalize_theta_set(v.args[1])) | |
| sets = Union(*new_sets) | |
| return Set.__new__(cls, sets) | |
| def expr(self): | |
| r, theta = self.variables | |
| return r*(cos(theta) + S.ImaginaryUnit*sin(theta)) | |
| class Complexes(CartesianComplexRegion, metaclass=Singleton): | |
| """ | |
| The :class:`Set` of all complex numbers | |
| Examples | |
| ======== | |
| >>> from sympy import S, I | |
| >>> S.Complexes | |
| Complexes | |
| >>> 1 + I in S.Complexes | |
| True | |
| See also | |
| ======== | |
| Reals | |
| ComplexRegion | |
| """ | |
| is_empty = False | |
| is_finite_set = False | |
| # Override property from superclass since Complexes has no args | |
| def sets(self): | |
| return ProductSet(S.Reals, S.Reals) | |
| def __new__(cls): | |
| return Set.__new__(cls) | |
Xet Storage Details
- Size:
- 48.2 kB
- Xet hash:
- 23335278f34759e53e231f2cc62a9fbf75c013459c5d740b851ee08b37a2cb18
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.